From bb6ab156c77fbbf44414146f14f64f9b571cbc18 Mon Sep 17 00:00:00 2001 From: crazycs Date: Thu, 14 May 2020 20:14:27 +0800 Subject: [PATCH 01/74] executor: make test stable (#17206) --- executor/executor_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/executor/executor_test.go b/executor/executor_test.go index f5056dc222cf2..520084ab63a31 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -4248,6 +4248,7 @@ func (s *testSplitTable) TestShowTableRegion(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") tk.MustExec("drop table if exists t_regions") + tk.MustExec("set global tidb_scatter_region = 1") atomic.StoreUint32(&ddl.EnableSplitTableRegion, 1) tk.MustExec("create table t_regions (a int key, b int, c int, index idx(b), index idx2(c))") // Test show table regions. From eb5927aa0873ac71857196bd23ce343d581a87d3 Mon Sep 17 00:00:00 2001 From: Zejun Li Date: Thu, 14 May 2020 20:27:13 +0800 Subject: [PATCH 02/74] kv: fix transaction too large error message (#17198) --- kv/memdb_buffer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kv/memdb_buffer.go b/kv/memdb_buffer.go index 0bb4ad049b459..2d7affcbd4a36 100644 --- a/kv/memdb_buffer.go +++ b/kv/memdb_buffer.go @@ -99,7 +99,7 @@ func (m *memDbBuffer) Set(k Key, v []byte) error { m.sandbox.Put(k, v) if m.Size() > int(m.bufferSizeLimit) { - return ErrTxnTooLarge.GenWithStackByArgs(m.Size()) + return ErrTxnTooLarge.GenWithStackByArgs(atomic.LoadUint64(&TxnTotalSizeLimit)) } return nil } @@ -108,7 +108,7 @@ func (m *memDbBuffer) Set(k Key, v []byte) error { func (m *memDbBuffer) Delete(k Key) error { m.sandbox.Put(k, nil) if m.Size() > int(m.bufferSizeLimit) { - return ErrTxnTooLarge.GenWithStackByArgs(m.Size()) + return ErrTxnTooLarge.GenWithStackByArgs(atomic.LoadUint64(&TxnTotalSizeLimit)) } return nil } From 1417ba2288b771aa2c8f804c9ea89aa638aae6a6 Mon Sep 17 00:00:00 2001 From: wjHuang Date: Thu, 14 May 2020 20:57:55 +0800 Subject: [PATCH 03/74] *: fix a bug that don't handle truncate properly for virtual generated column (#17217) --- executor/batch_checker.go | 2 +- executor/distsql.go | 2 +- executor/executor.go | 2 +- executor/insert.go | 2 +- executor/insert_common.go | 16 ++++++++-------- executor/point_get.go | 2 +- executor/table_reader.go | 2 +- executor/union_scan.go | 2 +- executor/update.go | 4 ++-- executor/write.go | 2 +- expression/integration_test.go | 31 +++++++++++++++++++++++++++++++ table/column.go | 21 +++++++++++++++------ table/column_test.go | 10 +++++----- util/rowDecoder/decoder.go | 2 +- 14 files changed, 70 insertions(+), 30 deletions(-) diff --git a/executor/batch_checker.go b/executor/batch_checker.go index 7efe92545c661..006e6ce902fae 100644 --- a/executor/batch_checker.go +++ b/executor/batch_checker.go @@ -198,7 +198,7 @@ func getOldRow(ctx context.Context, sctx sessionctx.Context, txn kv.Transaction, if err != nil { return nil, err } - oldRow[col.Offset], err = table.CastValue(sctx, val, col.ToInfo()) + oldRow[col.Offset], err = table.CastValue(sctx, val, col.ToInfo(), false) if err != nil { return nil, err } diff --git a/executor/distsql.go b/executor/distsql.go index faff88d0ea90c..9f69b91706749 100644 --- a/executor/distsql.go +++ b/executor/distsql.go @@ -868,7 +868,7 @@ func (w *tableWorker) compareData(ctx context.Context, task *lookupTableTask, ta if err != nil { return errors.Trace(err) } - val, err = table.CastValue(tblReaderExec.ctx, val, col.ColumnInfo) + val, err = table.CastValue(tblReaderExec.ctx, val, col.ColumnInfo, false) if err != nil { return errors.Trace(err) } diff --git a/executor/executor.go b/executor/executor.go index 769c8e62c8c95..e685bbd5921b9 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -1760,7 +1760,7 @@ func FillVirtualColumnValue(virtualRetTypes []*types.FieldType, virtualColumnInd } // Because the expression might return different type from // the generated column, we should wrap a CAST on the result. - castDatum, err := table.CastValue(sctx, datum, columns[idx]) + castDatum, err := table.CastValue(sctx, datum, columns[idx], true) if err != nil { return err } diff --git a/executor/insert.go b/executor/insert.go index 1e0b75fc26264..416917909d5ff 100644 --- a/executor/insert.go +++ b/executor/insert.go @@ -333,7 +333,7 @@ func (e *InsertExec) doDupRowUpdate(ctx context.Context, handle kv.Handle, oldRo if err1 != nil { return err1 } - e.row4Update[col.Col.Index], err1 = table.CastValue(e.ctx, val, col.Col.ToInfo()) + e.row4Update[col.Col.Index], err1 = table.CastValue(e.ctx, val, col.Col.ToInfo(), false) if err1 != nil { return err1 } diff --git a/executor/insert_common.go b/executor/insert_common.go index 05cce36c07a27..8d7636888f0ec 100644 --- a/executor/insert_common.go +++ b/executor/insert_common.go @@ -320,7 +320,7 @@ func (e *InsertValues) evalRow(ctx context.Context, list []expression.Expression if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } - val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo()) + val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo(), false) if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } @@ -349,7 +349,7 @@ func (e *InsertValues) fastEvalRow(ctx context.Context, list []expression.Expres if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } - val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo()) + val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo(), false) if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } @@ -473,7 +473,7 @@ func (e *InsertValues) getRow(ctx context.Context, vals []types.Datum) ([]types. row := make([]types.Datum, len(e.Table.Cols())) hasValue := make([]bool, len(e.Table.Cols())) for i, v := range vals { - casted, err := table.CastValue(e.ctx, v, e.insertColumns[i].ToInfo()) + casted, err := table.CastValue(e.ctx, v, e.insertColumns[i].ToInfo(), false) if e.handleErr(nil, &v, 0, err) != nil { return nil, err } @@ -576,7 +576,7 @@ func (e *InsertValues) fillRow(ctx context.Context, row []types.Datum, hasValue if e.handleErr(gCol, &val, 0, err) != nil { return nil, err } - row[colIdx], err = table.CastValue(e.ctx, val, gCol.ToInfo()) + row[colIdx], err = table.CastValue(e.ctx, val, gCol.ToInfo(), false) if err != nil { return nil, err } @@ -723,7 +723,7 @@ func (e *InsertValues) lazyAdjustAutoIncrementDatum(ctx context.Context, rows [] retryInfo.AddAutoIncrementID(id) // The value of d is adjusted by auto ID, so we need to cast it again. - d, err := table.CastValue(e.ctx, d, col.ToInfo()) + d, err := table.CastValue(e.ctx, d, col.ToInfo(), false) if err != nil { return nil, err } @@ -736,7 +736,7 @@ func (e *InsertValues) lazyAdjustAutoIncrementDatum(ctx context.Context, rows [] retryInfo.AddAutoIncrementID(recordID) // the value of d is adjusted by auto ID, so we need to cast it again. - autoDatum, err = table.CastValue(e.ctx, autoDatum, col.ToInfo()) + autoDatum, err = table.CastValue(e.ctx, autoDatum, col.ToInfo(), false) if err != nil { return nil, err } @@ -796,7 +796,7 @@ func (e *InsertValues) adjustAutoIncrementDatum(ctx context.Context, d types.Dat retryInfo.AddAutoIncrementID(recordID) // the value of d is adjusted by auto ID, so we need to cast it again. - casted, err := table.CastValue(e.ctx, d, c.ToInfo()) + casted, err := table.CastValue(e.ctx, d, c.ToInfo(), false) if err != nil { return types.Datum{}, err } @@ -878,7 +878,7 @@ func (e *InsertValues) adjustAutoRandomDatum(ctx context.Context, d types.Datum, d.SetAutoID(recordID, c.Flag) retryInfo.AddAutoRandomID(recordID) - casted, err := table.CastValue(e.ctx, d, c.ToInfo()) + casted, err := table.CastValue(e.ctx, d, c.ToInfo(), false) if err != nil { return types.Datum{}, err } diff --git a/executor/point_get.go b/executor/point_get.go index d6c713de3944e..32d137eb26ef3 100644 --- a/executor/point_get.go +++ b/executor/point_get.go @@ -313,7 +313,7 @@ func encodeIndexKey(e *baseExecutor, tblInfo *model.TableInfo, idxInfo *model.In str, err = idxVals[i].ToString() idxVals[i].SetString(str, colInfo.FieldType.Collate) } else { - idxVals[i], err = table.CastValue(e.ctx, idxVals[i], colInfo) + idxVals[i], err = table.CastValue(e.ctx, idxVals[i], colInfo, false) } if err != nil { return nil, err diff --git a/executor/table_reader.go b/executor/table_reader.go index 071d00381679b..47555653fa294 100644 --- a/executor/table_reader.go +++ b/executor/table_reader.go @@ -170,7 +170,7 @@ func (e *TableReaderExecutor) Next(ctx context.Context, req *chunk.Chunk) error err := FillVirtualColumnValue(e.virtualColumnRetFieldTypes, e.virtualColumnIndex, e.schema, e.columns, e.ctx, req) if err != nil { - return nil + return err } return nil diff --git a/executor/union_scan.go b/executor/union_scan.go index 3425356541e52..aae5708e48b5c 100644 --- a/executor/union_scan.go +++ b/executor/union_scan.go @@ -175,7 +175,7 @@ func (us *UnionScanExec) Next(ctx context.Context, req *chunk.Chunk) error { } // Because the expression might return different type from // the generated column, we should wrap a CAST on the result. - castDatum, err := table.CastValue(us.ctx, datum, us.columns[idx]) + castDatum, err := table.CastValue(us.ctx, datum, us.columns[idx], true) if err != nil { return err } diff --git a/executor/update.go b/executor/update.go index aeee211e4dbac..91ddd4fbae46a 100644 --- a/executor/update.go +++ b/executor/update.go @@ -217,7 +217,7 @@ func (e *UpdateExec) fastComposeNewRow(rowIdx int, oldRow []types.Datum, cols [] // info of `_tidb_rowid` column is nil. // No need to cast `_tidb_rowid` column value. if cols[assign.Col.Index] != nil { - val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo) + val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo, false) if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { return nil, err } @@ -244,7 +244,7 @@ func (e *UpdateExec) composeNewRow(rowIdx int, oldRow []types.Datum, cols []*tab // info of `_tidb_rowid` column is nil. // No need to cast `_tidb_rowid` column value. if cols[assign.Col.Index] != nil { - val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo) + val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo, false) if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { return nil, err } diff --git a/executor/write.go b/executor/write.go index 2d3252f8a3a3f..90e0e47a4a3d9 100644 --- a/executor/write.go +++ b/executor/write.go @@ -73,7 +73,7 @@ func updateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, old for i, col := range t.Cols() { if modified[i] { // Cast changed fields with respective columns. - v, err := table.CastValue(sctx, newData[i], col.ToInfo()) + v, err := table.CastValue(sctx, newData[i], col.ToInfo(), false) if err != nil { return false, err } diff --git a/expression/integration_test.go b/expression/integration_test.go index f3b940c67c6a6..56d3a2484eff5 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -6419,3 +6419,34 @@ func (s *testIntegrationSuite) TestIssue17098(c *C) { tk.MustExec("insert into t2 values('a');") tk.MustQuery("select collation(t1.a) from t1 union select collation(t2.a) from t2;").Check(testkit.Rows("utf8mb4_bin")) } + +func (s *testIntegrationSuite) TestIndexedVirtualGeneratedColumnTruncate(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(a int, b tinyint as(a+100) unique key)") + tk.MustExec("insert ignore into t values(200, default)") + tk.MustExec("update t set a=1 where a=200") + tk.MustExec("admin check table t") + tk.MustExec("delete from t") + tk.MustExec("insert ignore into t values(200, default)") + tk.MustExec("admin check table t") + tk.MustExec("insert ignore into t values(200, default) on duplicate key update a=100") + tk.MustExec("admin check table t") + tk.MustExec("delete from t") + tk.MustExec("admin check table t") + + tk.MustExec("begin") + tk.MustExec("insert ignore into t values(200, default)") + tk.MustExec("update t set a=1 where a=200") + tk.MustExec("admin check table t") + tk.MustExec("delete from t") + tk.MustExec("insert ignore into t values(200, default)") + tk.MustExec("admin check table t") + tk.MustExec("insert ignore into t values(200, default) on duplicate key update a=100") + tk.MustExec("admin check table t") + tk.MustExec("delete from t") + tk.MustExec("admin check table t") + tk.MustExec("commit") + tk.MustExec("admin check table t") +} diff --git a/table/column.go b/table/column.go index 5ddf268d0bd07..307e4c36ef0cd 100644 --- a/table/column.go +++ b/table/column.go @@ -143,7 +143,7 @@ func CastValues(ctx sessionctx.Context, rec []types.Datum, cols []*Column) (err sc := ctx.GetSessionVars().StmtCtx for _, c := range cols { var converted types.Datum - converted, err = CastValue(ctx, rec[c.Offset], c.ToInfo()) + converted, err = CastValue(ctx, rec[c.Offset], c.ToInfo(), false) if err != nil { if sc.DupKeyAsWarning { sc.AppendWarning(err) @@ -168,8 +168,12 @@ func handleWrongUtf8Value(ctx sessionctx.Context, col *model.ColumnInfo, casted } // CastValue casts a value based on column type. +// If forceIgnoreTruncate is true, the err returned will be always nil. +// It's safe now and it's the same as the behavior of select statement. +// Set it to true only in FillVirtualColumnValue and UnionScanExec.Next() +// If the handle of err is changed latter, the behavior of forceIgnoreTruncate also need to change. // TODO: change the third arg to TypeField. Not pass ColumnInfo. -func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo) (casted types.Datum, err error) { +func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo, forceIgnoreTruncate bool) (casted types.Datum, err error) { sc := ctx.GetSessionVars().StmtCtx casted, err = val.ConvertTo(sc, &col.FieldType) // TODO: make sure all truncate errors are handled by ConvertTo. @@ -182,7 +186,9 @@ func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo) ( } else { err = sc.HandleTruncate(err) } - if err != nil { + if forceIgnoreTruncate { + err = nil + } else if err != nil { return casted, err } @@ -216,6 +222,9 @@ func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo) ( w = width } + if forceIgnoreTruncate { + err = nil + } return casted, err } @@ -389,7 +398,7 @@ func EvalColDefaultExpr(ctx sessionctx.Context, col *model.ColumnInfo, defaultEx return types.Datum{}, err } // Check the evaluated data type by cast. - value, err := CastValue(ctx, d, col) + value, err := CastValue(ctx, d, col, false) if err != nil { return types.Datum{}, err } @@ -408,7 +417,7 @@ func getColDefaultExprValue(ctx sessionctx.Context, col *model.ColumnInfo, defau return types.Datum{}, err } // Check the evaluated data type by cast. - value, err := CastValue(ctx, d, col) + value, err := CastValue(ctx, d, col, false) if err != nil { return types.Datum{}, err } @@ -421,7 +430,7 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa } if col.Tp != mysql.TypeTimestamp && col.Tp != mysql.TypeDatetime { - value, err := CastValue(ctx, types.NewDatum(defaultVal), col) + value, err := CastValue(ctx, types.NewDatum(defaultVal), col, false) if err != nil { return types.Datum{}, err } diff --git a/table/column_test.go b/table/column_test.go index bef312cf1187f..904d12f86fff8 100644 --- a/table/column_test.go +++ b/table/column_test.go @@ -254,11 +254,11 @@ func (t *testTableSuite) TestCastValue(c *C) { State: model.StatePublic, } colInfo.Charset = mysql.UTF8Charset - val, err := CastValue(ctx, types.Datum{}, &colInfo) + val, err := CastValue(ctx, types.Datum{}, &colInfo, false) c.Assert(err, Equals, nil) c.Assert(val.GetInt64(), Equals, int64(0)) - val, err = CastValue(ctx, types.NewDatum("test"), &colInfo) + val, err = CastValue(ctx, types.NewDatum("test"), &colInfo, false) c.Assert(err, Not(Equals), nil) c.Assert(val.GetInt64(), Equals, int64(0)) @@ -278,16 +278,16 @@ func (t *testTableSuite) TestCastValue(c *C) { FieldType: *types.NewFieldType(mysql.TypeString), State: model.StatePublic, } - val, err = CastValue(ctx, types.NewDatum("test"), &colInfoS) + val, err = CastValue(ctx, types.NewDatum("test"), &colInfoS, false) c.Assert(err, IsNil) c.Assert(val, NotNil) colInfoS.Charset = mysql.UTF8Charset - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS, false) c.Assert(err, NotNil) colInfoS.Charset = mysql.UTF8MB4Charset - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS, false) c.Assert(err, NotNil) } diff --git a/util/rowDecoder/decoder.go b/util/rowDecoder/decoder.go index aa32fd395e0ad..5bc8e9e502aab 100644 --- a/util/rowDecoder/decoder.go +++ b/util/rowDecoder/decoder.go @@ -118,7 +118,7 @@ func (rd *RowDecoder) DecodeAndEvalRowWithMap(ctx sessionctx.Context, handle kv. if err != nil { return nil, err } - val, err = table.CastValue(ctx, val, col.Col.ColumnInfo) + val, err = table.CastValue(ctx, val, col.Col.ColumnInfo, false) if err != nil { return nil, err } From ea2e215ff8be8d99fecd5b1038454e66c6fead66 Mon Sep 17 00:00:00 2001 From: wjHuang Date: Fri, 15 May 2020 13:30:49 +0800 Subject: [PATCH 04/74] *: fix a bug caused by the wrong collation setting which leads to the wrong result of collation function (#17116) --- executor/set.go | 2 +- expression/builtin_info.go | 1 + expression/integration_test.go | 6 ++++++ session/session.go | 7 +------ sessionctx/variable/session.go | 21 ++++++++++++++++++++- 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/executor/set.go b/executor/set.go index 3ecc8fba9a938..0fed40596cb11 100644 --- a/executor/set.go +++ b/executor/set.go @@ -253,7 +253,7 @@ func (e *SetExecutor) setCharset(cs, co string) error { return errors.Trace(err) } } - return errors.Trace(sessionVars.SetSystemVar(variable.CollationConnection, co)) + return sessionVars.SetSystemVar(variable.CollationConnection, co) } func (e *SetExecutor) getVarValue(v *expression.VarAssignment, sysVar *variable.SysVar) (value types.Datum, err error) { diff --git a/expression/builtin_info.go b/expression/builtin_info.go index 252d043f19bc2..3007a50d1f5df 100644 --- a/expression/builtin_info.go +++ b/expression/builtin_info.go @@ -673,6 +673,7 @@ func (c *collationFunctionClass) getFunction(ctx sessionctx.Context, args []Expr if err != nil { return nil, err } + bf.tp.Charset, bf.tp.Collate = ctx.GetSessionVars().GetCharsetInfo() sig := &builtinCollationSig{bf} return sig, nil } diff --git a/expression/integration_test.go b/expression/integration_test.go index 56d3a2484eff5..dca7859159d71 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -6420,6 +6420,12 @@ func (s *testIntegrationSuite) TestIssue17098(c *C) { tk.MustQuery("select collation(t1.a) from t1 union select collation(t2.a) from t2;").Check(testkit.Rows("utf8mb4_bin")) } +func (s *testIntegrationSuite) TestIssue17115(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustQuery("select collation(user());").Check(testkit.Rows("utf8mb4_bin")) + tk.MustQuery("select collation(compress('abc'));").Check(testkit.Rows("binary")) +} + func (s *testIntegrationSuite) TestIndexedVirtualGeneratedColumnTruncate(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/session/session.go b/session/session.go index d3e9c3b55756f..86f233e5f584e 100644 --- a/session/session.go +++ b/session/session.go @@ -321,12 +321,7 @@ func (s *session) SetCollation(coID int) error { for _, v := range variable.SetNamesVariables { terror.Log(s.sessionVars.SetSystemVar(v, cs)) } - err = s.sessionVars.SetSystemVar(variable.CollationConnection, co) - if err != nil { - // Some clients may use the unsupported collations, such as utf8mb4_0900_ai_ci, We shouldn't return error or use the ERROR level log. - logutil.BgLogger().Warn(err.Error()) - } - return nil + return s.sessionVars.SetSystemVar(variable.CollationConnection, co) } func (s *session) PreparedPlanCache() *kvcache.SimpleLRUCache { diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 975f6dd0e7b25..21eb98d7cd115 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -29,6 +29,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/auth" + "github.com/pingcap/parser/charset" "github.com/pingcap/parser/mysql" "github.com/pingcap/parser/terror" pumpcli "github.com/pingcap/tidb-tools/tidb-binlog/pump_client" @@ -1245,7 +1246,25 @@ func (s *SessionVars) SetSystemVar(name string, val string) error { s.MetricSchemaRangeDuration = tidbOptInt64(val, DefTiDBMetricSchemaRangeDuration) case CollationConnection, CollationDatabase, CollationServer: if _, err := collate.GetCollationByName(val); err != nil { - return errors.Trace(err) + var ok bool + var charsetVal string + var err2 error + if name == CollationConnection { + charsetVal, ok = s.systems[CharacterSetConnection] + } else if name == CollationDatabase { + charsetVal, ok = s.systems[CharsetDatabase] + } else { + // CollationServer + charsetVal, ok = s.systems[CharacterSetServer] + } + if !ok { + return err + } + val, err2 = charset.GetDefaultCollation(charsetVal) + if err2 != nil { + return err2 + } + logutil.BgLogger().Warn(err.Error()) } case TiDBSlowLogThreshold: atomic.StoreUint64(&config.GetGlobalConfig().Log.SlowThreshold, uint64(tidbOptInt64(val, logutil.DefaultSlowThreshold))) From 9014ceee698a9976110bf2f3195d8da21612e4af Mon Sep 17 00:00:00 2001 From: Maxwell Date: Fri, 15 May 2020 14:38:19 +0800 Subject: [PATCH 05/74] ddl: fix bug when add expression index (#17151) --- ddl/db_integration_test.go | 6 ++++++ ddl/ddl_api.go | 16 ++++++++++++++++ ddl/index.go | 8 ++++++++ 3 files changed, 30 insertions(+) diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index fc46f3fbc898d..c9dfb1d1f6e23 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -2094,6 +2094,12 @@ func (s *testIntegrationSuite7) TestAddExpressionIndex(c *C) { tk.MustQuery("select * from t;").Check(testkit.Rows("1 2.1")) + // Issue #17111 + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a varchar(10), b varchar(10));") + tk.MustExec("alter table t1 add unique index ei_ab ((concat(a, b)));") + tk.MustExec("alter table t1 alter index ei_ab invisible;") + // Test experiment switch. config.GetGlobalConfig().Experimental.AllowsExpressionIndex = false tk.MustGetErrMsg("create index d on t((a+1))", "[ddl:8200]Unsupported creating expression index without allow-expression-index in config") diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index e25ddba05df5b..be3d7f33b818e 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -1133,16 +1133,32 @@ func getPrimaryKey(tblInfo *model.TableInfo) *model.IndexInfo { // table has explicit primary key return key } + // The case index without any columns should never happen, but still do a check here + if len(key.Columns) == 0 { + continue + } // find the first unique key with NOT NULL columns if implicitPK == nil && key.Unique { // ensure all columns in unique key have NOT NULL flag allColNotNull := true + skip := false for _, idxCol := range key.Columns { col := model.FindColumnInfo(tblInfo.Cols(), idxCol.Name.L) + // This index has a column in DeleteOnly state, + // or it is expression index (it defined on a hidden column), + // it can not be implicit PK, go to next index iterator + if col == nil || col.Hidden { + skip = true + break + } if !mysql.HasNotNullFlag(col.Flag) { allColNotNull = false + break } } + if skip { + continue + } if allColNotNull { implicitPK = key } diff --git a/ddl/index.go b/ddl/index.go index 1fa3c1c5f1923..aa36b220ec053 100755 --- a/ddl/index.go +++ b/ddl/index.go @@ -483,6 +483,14 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo indexInfo.Unique = unique indexInfo.ID = allocateIndexID(tblInfo) tblInfo.Indices = append(tblInfo.Indices, indexInfo) + + // Here we need do this check before set state to `DeleteOnly`, + // because if hidden columns has been set to `DeleteOnly`, + // the `DeleteOnly` columns are missing when we do this check. + if err := checkInvisibleIndexOnPK(tblInfo); err != nil { + job.State = model.JobStateCancelled + return ver, err + } logutil.BgLogger().Info("[ddl] run add index job", zap.String("job", job.String()), zap.Reflect("indexInfo", indexInfo)) } originalState := indexInfo.State From bcbc25db68a4dd719f02ffdf75e74e59b05a231d Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Fri, 15 May 2020 15:59:15 +0800 Subject: [PATCH 06/74] executor: make the aggregation function `distinct` consider collations (#17182) --- executor/aggfuncs/func_count_distinct.go | 3 +++ expression/integration_test.go | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/executor/aggfuncs/func_count_distinct.go b/executor/aggfuncs/func_count_distinct.go index 3773002e53b8a..6dc180f05b16a 100644 --- a/executor/aggfuncs/func_count_distinct.go +++ b/executor/aggfuncs/func_count_distinct.go @@ -24,6 +24,7 @@ import ( "github.com/pingcap/tidb/types/json" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/hack" "github.com/pingcap/tidb/util/set" "github.com/pingcap/tidb/util/stringutil" @@ -242,6 +243,7 @@ func (e *countOriginalWithDistinct4String) AppendFinalResult2Chunk(sctx sessionc func (e *countOriginalWithDistinct4String) UpdatePartialResult(sctx sessionctx.Context, rowsInGroup []chunk.Row, pr PartialResult) error { p := (*partialResult4CountDistinctString)(pr) + collator := collate.GetCollator(e.args[0].GetType().Collate) for _, row := range rowsInGroup { input, isNull, err := e.args[0].EvalString(sctx, row) @@ -251,6 +253,7 @@ func (e *countOriginalWithDistinct4String) UpdatePartialResult(sctx sessionctx.C if isNull { continue } + input = string(collator.Key(input)) if p.valSet.Exist(input) { continue diff --git a/expression/integration_test.go b/expression/integration_test.go index dca7859159d71..e7a915d45ee57 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -6097,6 +6097,17 @@ func (s *testIntegrationSerialSuite) TestCollateIndexLookup(c *C) { tk.MustQuery("select id from t_bin where v < 'b' and v > ' '").Sort().Check(testkit.Rows("1")) } +func (s *testIntegrationSerialSuite) TestIssue16668(c *C) { + collate.SetNewCollationEnabledForTest(true) + defer collate.SetNewCollationEnabledForTest(false) + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists tx") + tk.MustExec("CREATE TABLE `tx` ( `a` int(11) NOT NULL,`b` varchar(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL)") + tk.MustExec("insert into tx values (1, 'a'), (2, 'À'), (3, 'á'), (4, 'à'), (5, 'b'), (6, 'c'), (7, ' ')") + tk.MustQuery("select count(distinct(b)) from tx").Check(testkit.Rows("4")) +} + func (s *testIntegrationSerialSuite) TestCollateStringFunction(c *C) { collate.SetNewCollationEnabledForTest(true) defer collate.SetNewCollationEnabledForTest(false) From 78d69004ad70c3cf31a36a3eee8330452857634e Mon Sep 17 00:00:00 2001 From: wjHuang Date: Fri, 15 May 2020 16:49:22 +0800 Subject: [PATCH 07/74] ddl: consider collation when checks enum value in create table statement (#17177) --- ddl/ddl_api.go | 13 +++++++------ expression/integration_test.go | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index be3d7f33b818e..58b8efc6ebe8f 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -613,7 +613,7 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o if err != nil { return nil, nil, errors.Trace(err) } - err = checkColumnValueConstraint(col) + err = checkColumnValueConstraint(col, col.Collate) if err != nil { return nil, nil, errors.Trace(err) } @@ -881,21 +881,22 @@ func checkPriKeyConstraint(col *table.Column, hasDefaultValue, hasNullFlag bool, return nil } -func checkColumnValueConstraint(col *table.Column) error { +func checkColumnValueConstraint(col *table.Column, collation string) error { if col.Tp != mysql.TypeEnum && col.Tp != mysql.TypeSet { return nil } - valueMap := make(map[string]string, len(col.Elems)) + valueMap := make(map[string]bool, len(col.Elems)) + ctor := collate.GetCollator(collation) for i := range col.Elems { - val := strings.ToLower(col.Elems[i]) + val := string(ctor.Key(col.Elems[i])) if _, ok := valueMap[val]; ok { tpStr := "ENUM" if col.Tp == mysql.TypeSet { tpStr = "SET" } - return types.ErrDuplicatedValueInType.GenWithStackByArgs(col.Name, valueMap[val], tpStr) + return types.ErrDuplicatedValueInType.GenWithStackByArgs(col.Name, col.Elems[i], tpStr) } - valueMap[val] = col.Elems[i] + valueMap[val] = true } return nil } diff --git a/expression/integration_test.go b/expression/integration_test.go index e7a915d45ee57..9a64ecff79446 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -6431,6 +6431,21 @@ func (s *testIntegrationSuite) TestIssue17098(c *C) { tk.MustQuery("select collation(t1.a) from t1 union select collation(t2.a) from t2;").Check(testkit.Rows("utf8mb4_bin")) } +func (s *testIntegrationSerialSuite) TestIssue17176(c *C) { + collate.SetNewCollationEnabledForTest(true) + defer collate.SetNewCollationEnabledForTest(false) + + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustGetErrMsg("create table t(a enum('a', 'a ')) charset utf8 collate utf8_bin;", "[types:1291]Column 'a' has duplicated value 'a ' in ENUM") + tk.MustGetErrMsg("create table t(a enum('a', 'Á')) charset utf8 collate utf8_general_ci;", "[types:1291]Column 'a' has duplicated value 'Á' in ENUM") + tk.MustGetErrMsg("create table t(a enum('a', 'a ')) charset utf8mb4 collate utf8mb4_bin;", "[types:1291]Column 'a' has duplicated value 'a ' in ENUM") + tk.MustExec("create table t(a enum('a', 'A')) charset utf8 collate utf8_bin;") + tk.MustExec("drop table t;") + tk.MustExec("create table t3(a enum('a', 'A')) charset utf8mb4 collate utf8mb4_bin;") +} + func (s *testIntegrationSuite) TestIssue17115(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustQuery("select collation(user());").Check(testkit.Rows("utf8mb4_bin")) From f5706c9099681ca9910d33dd0033eaba20250171 Mon Sep 17 00:00:00 2001 From: crazycs Date: Fri, 15 May 2020 18:28:53 +0800 Subject: [PATCH 08/74] executor: fix slow_query bug when query with time equal condition (#17225) --- executor/executor_test.go | 21 +++++++++++++++++---- executor/slow_query.go | 6 ++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index 520084ab63a31..b49b183d8ea96 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -5382,19 +5382,26 @@ select 4;` select 5; # Time: 2020-02-17T18:00:05.000000+08:00 select 6;` + logData4 := ` +# Time: 2020-05-14T19:03:54.314615176+08:00 +select 7;` + fileName0 := "tidb-slow-2020-02-14T19-04-05.01.log" fileName1 := "tidb-slow-2020-02-15T19-04-05.01.log" fileName2 := "tidb-slow-2020-02-16T19-04-05.01.log" - fileName3 := "tidb-slow.log" + fileName3 := "tidb-slow-2020-02-17T18-00-05.01.log" + fileName4 := "tidb-slow.log" writeFile(fileName0, logData0) writeFile(fileName1, logData1) writeFile(fileName2, logData2) writeFile(fileName3, logData3) + writeFile(fileName4, logData4) defer func() { os.Remove(fileName0) os.Remove(fileName1) os.Remove(fileName2) os.Remove(fileName3) + os.Remove(fileName4) }() tk := testkit.NewTestKitWithInit(c, s.store) loc, err := time.LoadLocation("Asia/Shanghai") @@ -5408,7 +5415,7 @@ select 6;` }{ { sql: "select count(*),min(time),max(time) from %s where time > '2019-01-26 21:51:00' and time < now()", - result: []string{"6|2020-02-15 18:00:01.000000|2020-02-17 18:00:05.000000"}, + result: []string{"7|2020-02-15 18:00:01.000000|2020-05-14 19:03:54.314615"}, }, { sql: "select count(*),min(time),max(time) from %s where time > '2020-02-15 19:00:00' and time < '2020-02-16 18:00:02'", @@ -5424,7 +5431,7 @@ select 6;` }, { sql: "select count(*),min(time),max(time) from %s", - result: []string{"2|2020-02-16 19:00:00.000000|2020-02-17 18:00:05.000000"}, + result: []string{"1|2020-05-14 19:03:54.314615|2020-05-14 19:03:54.314615"}, }, { sql: "select count(*),min(time) from %s where time > '2020-02-16 20:00:00'", @@ -5436,7 +5443,7 @@ select 6;` }, { sql: "select query from %s where time > '2019-01-26 21:51:00' and time < now()", - result: []string{"select 1;", "select 2;", "select 3;", "select 4;", "select 5;", "select 6;"}, + result: []string{"select 1;", "select 2;", "select 3;", "select 4;", "select 5;", "select 6;", "select 7;"}, }, // Test for different timezone. { @@ -5449,6 +5456,12 @@ select 6;` sql: "select time from %s where time = '2020-02-17 12:00:05.000000'", result: []string{"2020-02-17 12:00:05.000000"}, }, + // Test for issue 17224 + { + prepareSQL: "set @@time_zone = '+08:00'", + sql: "select time from %s where time = '2020-05-14 19:03:54.314615'", + result: []string{"2020-05-14 19:03:54.314615"}, + }, } for _, cas := range cases { if len(cas.prepareSQL) > 0 { diff --git a/executor/slow_query.go b/executor/slow_query.go index 94040196cfe69..182fc150f23c5 100644 --- a/executor/slow_query.go +++ b/executor/slow_query.go @@ -610,7 +610,8 @@ func (e *slowQueryRetriever) getAllFiles(sctx sessionctx.Context, logFilePath st if err != nil { return handleErr(err) } - if fileStartTime.After(e.extractor.EndTime) { + start := types.NewTime(types.FromGoTime(fileStartTime), mysql.TypeDatetime, types.MaxFsp) + if start.Compare(e.checker.endTime) > 0 { return nil } @@ -619,7 +620,8 @@ func (e *slowQueryRetriever) getAllFiles(sctx sessionctx.Context, logFilePath st if err != nil { return handleErr(err) } - if fileEndTime.Before(e.extractor.StartTime) { + end := types.NewTime(types.FromGoTime(fileEndTime), mysql.TypeDatetime, types.MaxFsp) + if end.Compare(e.checker.startTime) < 0 { return nil } _, err = file.Seek(0, io.SeekStart) From 0de6925e124a4537d926aca9a777512696a37547 Mon Sep 17 00:00:00 2001 From: Maxwell Date: Fri, 15 May 2020 21:28:44 +0800 Subject: [PATCH 09/74] ddl: Add some limit for `auto_random` (#17119) --- ddl/ddl_api.go | 10 +++- ddl/serial_test.go | 86 +++++++++++++-------------- executor/ddl_test.go | 27 ++++----- executor/insert_test.go | 8 +-- executor/seqtest/seq_executor_test.go | 6 +- executor/write.go | 8 ++- infoschema/tables_test.go | 4 +- meta/autoid/autoid.go | 35 +++++++---- meta/autoid/errors.go | 4 +- 9 files changed, 99 insertions(+), 89 deletions(-) diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 58b8efc6ebe8f..42d973f87b995 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -1176,6 +1176,10 @@ func setTableAutoRandomBits(ctx sessionctx.Context, tbInfo *model.TableInfo, col if !allowAutoRandom { return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomExperimentalDisabledErrMsg) } + if col.Tp.Tp != mysql.TypeLonglong { + return ErrInvalidAutoRandom.GenWithStackByArgs( + fmt.Sprintf(autoid.AutoRandomOnNonBigIntColumn, types.TypeStr(col.Tp.Tp))) + } if !tbInfo.PKIsHandle || col.Name.Name.L != pkColName.L { errMsg := fmt.Sprintf(autoid.AutoRandomPKisNotHandleErrMsg, col.Name.Name.O) return ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) @@ -1195,9 +1199,9 @@ func setTableAutoRandomBits(ctx sessionctx.Context, tbInfo *model.TableInfo, col layout := autoid.NewAutoRandomIDLayout(col.Tp, autoRandBits) if autoRandBits == 0 { return ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomNonPositive) - } else if autoRandBits >= layout.TypeBitsLength { - errMsg := fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, col.Name.Name.L, - layout.TypeBitsLength, autoRandBits, col.Name.Name.L, layout.TypeBitsLength-1) + } else if autoRandBits > autoid.MaxAutoRandomBits { + errMsg := fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, + autoid.MaxAutoRandomBits, autoRandBits, col.Name.Name.O) return ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) } tbInfo.AutoRandomBits = autoRandBits diff --git a/ddl/serial_test.go b/ddl/serial_test.go index 0413361578f39..425f364ff6252 100644 --- a/ddl/serial_test.go +++ b/ddl/serial_test.go @@ -834,11 +834,11 @@ func (s *testSerialSuite) TestAutoRandom(c *C) { assertWithAutoInc := func(sql string) { assertInvalidAutoRandomErr(sql, autoid.AutoRandomIncompatibleWithAutoIncErrMsg) } - assertOverflow := func(sql, colType string, autoRandBits, maxFieldLength uint64) { - assertInvalidAutoRandomErr(sql, autoid.AutoRandomOverflowErrMsg, colType, maxFieldLength, autoRandBits, colType, maxFieldLength-1) + assertOverflow := func(sql, colName string, autoRandBits uint64) { + assertInvalidAutoRandomErr(sql, autoid.AutoRandomOverflowErrMsg, autoid.MaxAutoRandomBits, autoRandBits, colName) } assertModifyColType := func(sql string) { - assertInvalidAutoRandomErr(sql, autoid.AutoRandomModifyColTypeErrMsg) + tk.MustGetErrCode(sql, errno.ErrUnsupportedDDLOperation) } assertDefault := func(sql string) { assertInvalidAutoRandomErr(sql, autoid.AutoRandomIncompatibleWithDefaultValueErrMsg) @@ -846,6 +846,9 @@ func (s *testSerialSuite) TestAutoRandom(c *C) { assertNonPositive := func(sql string) { assertInvalidAutoRandomErr(sql, autoid.AutoRandomNonPositive) } + assertBigIntOnly := func(sql, colType string) { + assertInvalidAutoRandomErr(sql, autoid.AutoRandomOnNonBigIntColumn, colType) + } mustExecAndDrop := func(sql string, fns ...func()) { tk.MustExec(sql) for _, f := range fns { @@ -856,21 +859,22 @@ func (s *testSerialSuite) TestAutoRandom(c *C) { testutil.ConfigTestUtils.SetupAutoRandomTestConfig() defer testutil.ConfigTestUtils.RestoreAutoRandomTestConfig() + + // Only bigint column can set auto_random + assertBigIntOnly("create table t (a char primary key auto_random(3), b int)", "char") + assertBigIntOnly("create table t (a varchar(255) primary key auto_random(3), b int)", "varchar") + assertBigIntOnly("create table t (a timestamp primary key auto_random(3), b int)", "timestamp") + // PKIsHandle, but auto_random is defined on non-primary key. - assertPKIsNotHandle("create table t (a bigint auto_random (3) primary key, b int auto_random (3))", "b") - assertPKIsNotHandle("create table t (a bigint auto_random (3), b int auto_random(3), primary key(a))", "b") - assertPKIsNotHandle("create table t (a bigint auto_random (3), b int auto_random(3) primary key)", "a") + assertPKIsNotHandle("create table t (a bigint auto_random (3) primary key, b bigint auto_random (3))", "b") + assertPKIsNotHandle("create table t (a bigint auto_random (3), b bigint auto_random(3), primary key(a))", "b") + assertPKIsNotHandle("create table t (a bigint auto_random (3), b bigint auto_random(3) primary key)", "a") // PKIsNotHandle: no primary key. - assertPKIsNotHandle("create table t (a int auto_random(3), b int)", "a") assertPKIsNotHandle("create table t (a bigint auto_random(3), b int)", "a") - // PKIsNotHandle: primary key is not integer column. - assertPKIsNotHandle("create table t (a char primary key auto_random(3), b int)", "a") - assertPKIsNotHandle("create table t (a varchar(255) primary key auto_random(3), b int)", "a") - assertPKIsNotHandle("create table t (a timestamp primary key auto_random(3), b int)", "a") // PKIsNotHandle: primary key is not a single column. - assertPKIsNotHandle("create table t (a bigint auto_random(3), b int, primary key (a, b))", "a") - assertPKIsNotHandle("create table t (a int auto_random(3), b int, c char, primary key (a, c))", "a") + assertPKIsNotHandle("create table t (a bigint auto_random(3), b bigint, primary key (a, b))", "a") + assertPKIsNotHandle("create table t (a bigint auto_random(3), b int, c char, primary key (a, c))", "a") // Can not set auto_random along with auto_increment. assertWithAutoInc("create table t (a bigint auto_random(3) primary key auto_increment)") @@ -878,45 +882,39 @@ func (s *testSerialSuite) TestAutoRandom(c *C) { assertWithAutoInc("create table t (a bigint auto_increment primary key auto_random(3))") assertWithAutoInc("create table t (a bigint auto_random(3) auto_increment, primary key (a))") - // Overflow data type max length. - assertOverflow("create table t (a bigint auto_random(65) primary key)", "a", 65, 64) - assertOverflow("create table t (a int auto_random(33) primary key)", "a", 33, 32) - assertOverflow("create table t (a mediumint auto_random(25) primary key)", "a", 25, 24) - assertOverflow("create table t (a smallint auto_random(17) primary key)", "a", 17, 16) - assertOverflow("create table t (a tinyint auto_random(9) primary key)", "a", 9, 8) - - assertNonPositive("create table t (a bigint auto_random(0) primary key)") - // Can not set auto_random along with default. - assertDefault("create table t (a int auto_random primary key default 3)") + assertDefault("create table t (a bigint auto_random primary key default 3)") assertDefault("create table t (a bigint auto_random(2) primary key default 5)") - mustExecAndDrop("create table t (a int auto_random primary key)", func() { - assertDefault("alter table t modify column a int auto_random default 3") + mustExecAndDrop("create table t (a bigint auto_random primary key)", func() { + assertDefault("alter table t modify column a bigint auto_random default 3") }) - // Basic usage. - mustExecAndDrop("create table t (a bigint auto_random(4) primary key, b varchar(255))") - mustExecAndDrop("create table t (a bigint primary key auto_random(4), b varchar(255))") - mustExecAndDrop("create table t (a bigint auto_random(4), b varchar(255), primary key (a))") + // Overflow data type max length. + assertOverflow("create table t (a bigint auto_random(64) primary key)", "a", 64) + assertOverflow("create table t (a bigint auto_random(16) primary key)", "a", 16) - // Different primary key field types. + assertNonPositive("create table t (a bigint auto_random(0) primary key)") + tk.MustGetErrMsg("create table t (a bigint auto_random(-1) primary key)", + `[parser:1064]You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 38 near "-1) primary key)" `) + + // Basic usage. + mustExecAndDrop("create table t (a bigint auto_random(1) primary key)") mustExecAndDrop("create table t (a bigint auto_random(4) primary key)") - mustExecAndDrop("create table t (a int auto_random(4) primary key)") - mustExecAndDrop("create table t (a mediumint auto_random(4) primary key)") - mustExecAndDrop("create table t (a smallint auto_random(4) primary key)") - mustExecAndDrop("create table t (a tinyint auto_random(4) primary key)") + mustExecAndDrop("create table t (a bigint auto_random(15) primary key)") + mustExecAndDrop("create table t (a bigint primary key auto_random(4))") + mustExecAndDrop("create table t (a bigint auto_random(4), primary key (a))") // Auto_random can occur multiple times like other column attributes. mustExecAndDrop("create table t (a bigint auto_random(3) auto_random(2) primary key)") - mustExecAndDrop("create table t (a int, b bigint auto_random(3) primary key auto_random(2))") - mustExecAndDrop("create table t (a int auto_random(1) auto_random(2) auto_random(3), primary key (a))") + mustExecAndDrop("create table t (a bigint, b bigint auto_random(3) primary key auto_random(2))") + mustExecAndDrop("create table t (a bigint auto_random(1) auto_random(2) auto_random(3), primary key (a))") // Add/drop the auto_random attribute is not allowed. mustExecAndDrop("create table t (a bigint auto_random(3) primary key)", func() { assertAlterValue("alter table t modify column a bigint") assertAlterValue("alter table t change column a b bigint") }) - mustExecAndDrop("create table t (a int, b char, c int auto_random(3), primary key(c))", func() { + mustExecAndDrop("create table t (a bigint, b char, c bigint auto_random(3), primary key(c))", func() { assertAlterValue("alter table t modify column c bigint") assertAlterValue("alter table t change column c d bigint") }) @@ -926,7 +924,10 @@ func (s *testSerialSuite) TestAutoRandom(c *C) { }) // Modifying the field type of a auto_random column is not allowed. - mustExecAndDrop("create table t (a tinyint primary key auto_random(3))", func() { + // Here the throw error is `ERROR 8200 (HY000): Unsupported modify column: length 11 is less than origin 20`, + // instead of `ERROR 8216 (HY000): Invalid auto random: modifying the auto_random column type is not supported` + // Because the origin column is `bigint`, it can not change to any other column type in TiDB limitation. + mustExecAndDrop("create table t (a bigint primary key auto_random(3))", func() { assertModifyColType("alter table t modify column a int auto_random(3)") assertModifyColType("alter table t modify column a mediumint auto_random(3)") assertModifyColType("alter table t modify column a smallint auto_random(3)") @@ -941,13 +942,8 @@ func (s *testSerialSuite) TestAutoRandom(c *C) { c.Assert(tk.Se.GetSessionVars().StmtCtx.WarningCount(), Equals, uint16(0)) }) } - assertShowWarningCorrect("create table t (a tinyint unsigned auto_random(6) primary key)", 3) - assertShowWarningCorrect("create table t (a tinyint unsigned auto_random(5) primary key)", 7) - assertShowWarningCorrect("create table t (a tinyint auto_random(4) primary key)", 7) - assertShowWarningCorrect("create table t (a bigint auto_random(62) primary key)", 1) - assertShowWarningCorrect("create table t (a bigint unsigned auto_random(61) primary key)", 7) - assertShowWarningCorrect("create table t (a int auto_random(30) primary key)", 1) - assertShowWarningCorrect("create table t (a int auto_random(29) primary key)", 3) + assertShowWarningCorrect("create table t (a bigint auto_random(15) primary key)", 281474976710655) + assertShowWarningCorrect("create table t (a bigint unsigned auto_random(15) primary key)", 562949953421311) // Disallow using it when allow-auto-random is not enabled. config.GetGlobalConfig().Experimental.AllowAutoRandom = false diff --git a/executor/ddl_test.go b/executor/ddl_test.go index fcda182a6504f..4413acc56d85b 100644 --- a/executor/ddl_test.go +++ b/executor/ddl_test.go @@ -869,9 +869,10 @@ func (s *testAutoRandomSuite) TestAutoRandomBitsData(c *C) { } // Test explicit insert. - tk.MustExec("create table t (a tinyint primary key auto_random(2), b int)") - for i := 1; i <= 100; i++ { - tk.MustExec("insert into t values (?, ?)", i, i) + autoRandBitsUpperBound := 2<<47 - 1 + tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") + for i := -10; i < 10; i++ { + tk.MustExec(fmt.Sprintf("insert into t values(%d, %d)", i+autoRandBitsUpperBound, i)) } _, err = tk.Exec("insert into t (b) values (0)") c.Assert(err, NotNil) @@ -879,26 +880,18 @@ func (s *testAutoRandomSuite) TestAutoRandomBitsData(c *C) { tk.MustExec("drop table t") // Test overflow. - tk.MustExec("create table t (a tinyint primary key auto_random(2), b int)") - for i := 0; i < 31; /* 2^(8-2-1)-1 */ i++ { - tk.MustExec(fmt.Sprintf("insert into t (b) values (%d)", i)) - } - _, err = tk.Exec("insert into t (b) values (0)") - c.Assert(err, NotNil) - c.Assert(err.Error(), Equals, autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) - tk.MustExec("drop table t") - - // Test rebase. - tk.MustExec("create table t (a tinyint primary key auto_random(2), b int)") - tk.MustExec("insert into t values (31, 2)") + tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") + // Here we cannot fill the all values for a `bigint` column, + // so firstly we rebase auto_rand to the position before overflow. + tk.MustExec(fmt.Sprintf("insert into t values (%d, %d)", autoRandBitsUpperBound, 1)) _, err = tk.Exec("insert into t (b) values (0)") c.Assert(err, NotNil) c.Assert(err.Error(), Equals, autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) tk.MustExec("drop table t") - tk.MustExec("create table t (a tinyint primary key auto_random(2), b int)") + tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") tk.MustExec("insert into t values (1, 2)") - tk.MustExec("update t set a = 31 where a = 1") + tk.MustExec(fmt.Sprintf("update t set a = %d where a = 1", autoRandBitsUpperBound)) _, err = tk.Exec("insert into t (b) values (0)") c.Assert(err, NotNil) c.Assert(err.Error(), Equals, autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) diff --git a/executor/insert_test.go b/executor/insert_test.go index d6e912f9591b4..666eb21c9dc35 100644 --- a/executor/insert_test.go +++ b/executor/insert_test.go @@ -978,7 +978,7 @@ func (s *testSuite9) TestAutoRandomID(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec(`use test`) tk.MustExec(`drop table if exists ar`) - tk.MustExec(`create table ar (id int key auto_random, name char(10))`) + tk.MustExec(`create table ar (id bigint key auto_random, name char(10))`) tk.MustExec(`insert into ar(id) values (null)`) rs := tk.MustQuery(`select id from ar`) @@ -1021,7 +1021,7 @@ func (s *testSuite9) TestMultiAutoRandomID(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec(`use test`) tk.MustExec(`drop table if exists ar`) - tk.MustExec(`create table ar (id int key auto_random, name char(10))`) + tk.MustExec(`create table ar (id bigint key auto_random, name char(10))`) tk.MustExec(`insert into ar(id) values (null),(null),(null)`) rs := tk.MustQuery(`select id from ar order by id`) @@ -1070,7 +1070,7 @@ func (s *testSuite9) TestAutoRandomIDAllowZero(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec(`use test`) tk.MustExec(`drop table if exists ar`) - tk.MustExec(`create table ar (id int key auto_random, name char(10))`) + tk.MustExec(`create table ar (id bigint key auto_random, name char(10))`) rs := tk.MustQuery(`select @@session.sql_mode`) sqlMode := rs.Rows()[0][0].(string) @@ -1108,7 +1108,7 @@ func (s *testSuite9) TestAutoRandomIDExplicit(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec(`use test`) tk.MustExec(`drop table if exists ar`) - tk.MustExec(`create table ar (id int key auto_random, name char(10))`) + tk.MustExec(`create table ar (id bigint key auto_random, name char(10))`) tk.MustExec(`insert into ar(id) values (1)`) tk.MustQuery(`select id from ar`).Check(testkit.Rows("1")) diff --git a/executor/seqtest/seq_executor_test.go b/executor/seqtest/seq_executor_test.go index d2d3ab3577cef..371be9894178e 100644 --- a/executor/seqtest/seq_executor_test.go +++ b/executor/seqtest/seq_executor_test.go @@ -842,7 +842,7 @@ func HelperTestAdminShowNextID(c *C, s *seqTestSuite, str string) { }() // Test for a table with auto_random primary key. - tk.MustExec("create table t3(id int primary key auto_random(5), c int)") + tk.MustExec("create table t3(id bigint primary key auto_random(5), c int)") // Start handle is 1. r = tk.MustQuery(str + " t3 next_row_id") r.Check(testkit.Rows("test1 t3 _tidb_rowid 1 AUTO_INCREMENT", "test1 t3 id 1 AUTO_RANDOM")) @@ -1296,7 +1296,7 @@ func (s *seqTestSuite) TestAutoRandIDRetry(c *C) { tk.MustExec("create database if not exists auto_random_retry") tk.MustExec("use auto_random_retry") tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int auto_random(3) primary key)") + tk.MustExec("create table t (id bigint auto_random(3) primary key)") extractMaskedOrderedHandles := func() []int64 { handles, err := ddltestutil.ExtractAllTableHandles(tk.Se, "auto_random_retry", "t") @@ -1372,7 +1372,7 @@ func (s *seqTestSuite) TestAutoRandRecoverTable(c *C) { defer autoid.SetStep(stp) // Check rebase auto_random id. - tk.MustExec("create table t_recover_auto_rand (a int auto_random(5) primary key);") + tk.MustExec("create table t_recover_auto_rand (a bigint auto_random(5) primary key);") tk.MustExec("insert into t_recover_auto_rand values (),(),()") tk.MustExec("drop table t_recover_auto_rand") tk.MustExec("recover table t_recover_auto_rand") diff --git a/executor/write.go b/executor/write.go index 90e0e47a4a3d9..76b8c4a5e42d4 100644 --- a/executor/write.go +++ b/executor/write.go @@ -214,8 +214,12 @@ func rebaseAutoRandomValue(sctx sessionctx.Context, t table.Table, newData *type if err != nil { return err } - shardBits := tableInfo.AutoRandomBits + 1 // sign bit is reserved. - recordID = recordID << shardBits >> shardBits + if recordID < 0 { + return nil + } + layout := autoid.NewAutoRandomIDLayout(&col.FieldType, tableInfo.AutoRandomBits) + // Set bits except incremental_bits to zero. + recordID = recordID & (1< Date: Fri, 15 May 2020 21:53:32 +0800 Subject: [PATCH 10/74] Rename enabled -> enable (#17227) --- config/config.go | 4 ++-- config/config.toml.example | 2 +- store/tikv/coprocessor_cache.go | 2 +- store/tikv/coprocessor_cache_test.go | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index df6fe4047d9d8..55f9b2890dd2d 100644 --- a/config/config.go +++ b/config/config.go @@ -476,7 +476,7 @@ type TiKVClient struct { type CoprocessorCache struct { // Whether to enable the copr cache. The copr cache saves the result from TiKV Coprocessor in the memory and // reuses the result when corresponding data in TiKV is unchanged, on a region basis. - Enabled bool `toml:"enabled" json:"enabled"` + Enable bool `toml:"enable" json:"enable"` // The capacity in MB of the cache. CapacityMB float64 `toml:"capacity-mb" json:"capacity-mb"` // Only cache requests whose result set is small. @@ -652,7 +652,7 @@ var defaultConf = Config{ StoreLivenessTimeout: DefStoreLivenessTimeout, CoprCache: CoprocessorCache{ - Enabled: true, + Enable: true, CapacityMB: 1000, AdmissionMaxResultMB: 10, AdmissionMinProcessMs: 5, diff --git a/config/config.toml.example b/config/config.toml.example index d9754ba803808..ef6fc1fa0782b 100644 --- a/config/config.toml.example +++ b/config/config.toml.example @@ -357,7 +357,7 @@ store-liveness-timeout = "120s" [tikv-client.copr-cache] # Whether to enable the copr cache. The copr cache saves the result from TiKV Coprocessor in the memory and # reuses the result when corresponding data in TiKV is unchanged, on a region basis. -enabled = true +enable = true # The capacity in MB of the cache. capacity-mb = 1000.0 diff --git a/store/tikv/coprocessor_cache.go b/store/tikv/coprocessor_cache.go index 318fef494e0a5..771f75b96ec47 100644 --- a/store/tikv/coprocessor_cache.go +++ b/store/tikv/coprocessor_cache.go @@ -56,7 +56,7 @@ func (v *coprCacheValue) Len() int { } func newCoprCache(config *config.CoprocessorCache) (*coprCache, error) { - if config == nil || !config.Enabled { + if config == nil || !config.Enable { return nil, nil } capacityInBytes := int64(config.CapacityMB * 1024.0 * 1024.0) diff --git a/store/tikv/coprocessor_cache_test.go b/store/tikv/coprocessor_cache_test.go index dd75610a55f96..66125da999b3c 100644 --- a/store/tikv/coprocessor_cache_test.go +++ b/store/tikv/coprocessor_cache_test.go @@ -73,7 +73,7 @@ func (s *testCoprocessorSuite) TestBuildCacheKey(c *C) { } func (s *testCoprocessorSuite) TestDisable(c *C) { - cache, err := newCoprCache(&config.CoprocessorCache{Enabled: false}) + cache, err := newCoprCache(&config.CoprocessorCache{Enable: false}) c.Assert(err, IsNil) c.Assert(cache, IsNil) @@ -86,21 +86,21 @@ func (s *testCoprocessorSuite) TestDisable(c *C) { v = cache.CheckAdmission(1024, time.Second*5) c.Assert(v, Equals, false) - cache, err = newCoprCache(&config.CoprocessorCache{Enabled: true, CapacityMB: 0, AdmissionMaxResultMB: 1}) + cache, err = newCoprCache(&config.CoprocessorCache{Enable: true, CapacityMB: 0, AdmissionMaxResultMB: 1}) c.Assert(err, NotNil) c.Assert(cache, IsNil) - cache, err = newCoprCache(&config.CoprocessorCache{Enabled: true, CapacityMB: 0.001}) + cache, err = newCoprCache(&config.CoprocessorCache{Enable: true, CapacityMB: 0.001}) c.Assert(err, NotNil) c.Assert(cache, IsNil) - cache, err = newCoprCache(&config.CoprocessorCache{Enabled: true, CapacityMB: 0.001, AdmissionMaxResultMB: 1}) + cache, err = newCoprCache(&config.CoprocessorCache{Enable: true, CapacityMB: 0.001, AdmissionMaxResultMB: 1}) c.Assert(err, IsNil) c.Assert(cache, NotNil) } func (s *testCoprocessorSuite) TestAdmission(c *C) { - cache, err := newCoprCache(&config.CoprocessorCache{Enabled: true, AdmissionMinProcessMs: 5, AdmissionMaxResultMB: 1, CapacityMB: 1}) + cache, err := newCoprCache(&config.CoprocessorCache{Enable: true, AdmissionMinProcessMs: 5, AdmissionMaxResultMB: 1, CapacityMB: 1}) c.Assert(err, IsNil) c.Assert(cache, NotNil) @@ -155,7 +155,7 @@ func (s *testCoprocessorSuite) TestCacheValueLen(c *C) { } func (s *testCoprocessorSuite) TestGetSet(c *C) { - cache, err := newCoprCache(&config.CoprocessorCache{Enabled: true, AdmissionMinProcessMs: 5, AdmissionMaxResultMB: 1, CapacityMB: 1}) + cache, err := newCoprCache(&config.CoprocessorCache{Enable: true, AdmissionMinProcessMs: 5, AdmissionMaxResultMB: 1, CapacityMB: 1}) c.Assert(err, IsNil) c.Assert(cache, NotNil) From 51015c1c21f36e792baacad63f15f1775c016c3f Mon Sep 17 00:00:00 2001 From: Lynn Date: Mon, 18 May 2020 13:23:47 +0800 Subject: [PATCH 11/74] *: fix the way to use `recover` in ddl, and unify using `recover` in domain and ddl (#16798) --- ddl/ddl.go | 34 ++++------------------------------ ddl/ddl_test.go | 22 ++-------------------- ddl/ddl_worker.go | 23 +++++++++++++++-------- ddl/index.go | 18 ++++-------------- ddl/util/syncer.go | 3 +++ domain/domain.go | 37 ++++++++++--------------------------- util/misc.go | 30 ++++++++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 99 deletions(-) diff --git a/ddl/ddl.go b/ddl/ddl.go index de88e90607d7e..8a68cda3e825a 100644 --- a/ddl/ddl.go +++ b/ddl/ddl.go @@ -41,7 +41,6 @@ import ( "github.com/pingcap/tidb/sessionctx/binloginfo" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/table" - tidbutil "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/logutil" "go.uber.org/zap" ) @@ -318,18 +317,7 @@ func (d *ddl) Start(ctxPool *pools.ResourcePool) error { d.quitCh = make(chan struct{}) d.wg.Add(1) - go func() { - defer d.wg.Done() - tidbutil.WithRecovery( - func() { d.limitDDLJobs() }, - func(r interface{}) { - if r != nil { - logutil.BgLogger().Error("[ddl] limit DDL jobs meet panic", - zap.String("ID", d.uuid), zap.Reflect("r", r), zap.Stack("stack trace")) - metrics.PanicCounter.WithLabelValues(metrics.LabelDDL).Inc() - } - }) - }() + go d.limitDDLJobs() // If RunWorker is true, we need campaign owner and do DDL job. // Otherwise, we needn't do that. @@ -347,14 +335,8 @@ func (d *ddl) Start(ctxPool *pools.ResourcePool) error { for _, worker := range d.workers { worker.wg.Add(1) w := worker - go tidbutil.WithRecovery( - func() { w.start(d.ddlCtx) }, - func(r interface{}) { - if r != nil { - logutil.Logger(w.logCtx).Error("[ddl] DDL worker meet panic", zap.String("ID", d.uuid)) - metrics.PanicCounter.WithLabelValues(metrics.LabelDDLWorker).Inc() - } - }) + go w.start(d.ddlCtx) + metrics.DDLCounter.WithLabelValues(fmt.Sprintf("%s_%s", metrics.CreateDDL, worker.String())).Inc() // When the start function is called, we will send a fake job to let worker @@ -362,15 +344,7 @@ func (d *ddl) Start(ctxPool *pools.ResourcePool) error { asyncNotify(worker.ddlJobCh) } - go tidbutil.WithRecovery( - func() { d.schemaSyncer.StartCleanWork() }, - func(r interface{}) { - if r != nil { - logutil.BgLogger().Error("[ddl] DDL syncer clean worker meet panic", - zap.String("ID", d.uuid), zap.Reflect("r", r), zap.Stack("stack trace")) - metrics.PanicCounter.WithLabelValues(metrics.LabelDDLSyncer).Inc() - } - }) + go d.schemaSyncer.StartCleanWork() metrics.DDLCounter.WithLabelValues(metrics.StartCleanWork).Inc() } diff --git a/ddl/ddl_test.go b/ddl/ddl_test.go index e04aac1b43839..03b4baaf73bbc 100644 --- a/ddl/ddl_test.go +++ b/ddl/ddl_test.go @@ -20,7 +20,6 @@ import ( "time" . "github.com/pingcap/check" - "github.com/pingcap/log" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" "github.com/pingcap/parser/terror" @@ -28,15 +27,12 @@ import ( "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/meta" "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/metrics" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/mock" "github.com/pingcap/tidb/util/testleak" - "go.uber.org/zap" ) type DDLForTest interface { @@ -73,16 +69,7 @@ func (d *ddl) restartWorkers(ctx context.Context) { d.quitCh = make(chan struct{}) d.wg.Add(1) - go func() { - defer d.wg.Done() - util.WithRecovery( - func() { d.limitDDLJobs() }, - func(r interface{}) { - logutil.BgLogger().Error("[ddl] DDL add batch DDL jobs meet panic", - zap.String("ID", d.uuid), zap.Reflect("r", r), zap.Stack("stack trace")) - metrics.PanicCounter.WithLabelValues(metrics.LabelDDL).Inc() - }) - }() + go d.limitDDLJobs() if !RunWorker { return } @@ -93,12 +80,7 @@ func (d *ddl) restartWorkers(ctx context.Context) { worker.wg.Add(1) worker.quitCh = make(chan struct{}) w := worker - go util.WithRecovery(func() { w.start(d.ddlCtx) }, - func(r interface{}) { - if r != nil { - log.Error("[ddl] restart DDL worker meet panic", zap.String("worker", w.String()), zap.String("ID", d.uuid)) - } - }) + go w.start(d.ddlCtx) asyncNotify(worker.ddlJobCh) } } diff --git a/ddl/ddl_worker.go b/ddl/ddl_worker.go index 5be50a071856a..344646ff4e681 100644 --- a/ddl/ddl_worker.go +++ b/ddl/ddl_worker.go @@ -129,6 +129,11 @@ func (w *worker) close() { func (w *worker) start(d *ddlCtx) { logutil.Logger(w.logCtx).Info("[ddl] start DDL worker") defer w.wg.Done() + defer tidbutil.Recover( + metrics.LabelDDLWorker, + fmt.Sprintf("DDL ID %s, %s start", d.uuid, w), + nil, true, + ) // We use 4 * lease time to check owner's timeout, so here, we will update owner's status // every 2 * lease time. If lease is 0, we will use default 1s. @@ -196,6 +201,9 @@ func buildJobDependence(t *meta.Meta, curJob *model.Job) error { } func (d *ddl) limitDDLJobs() { + defer d.wg.Done() + defer tidbutil.Recover(metrics.LabelDDL, "limitDDLJobs", nil, true) + tasks := make([]*limitJobTask, 0, batchAddingJobs) for { select { @@ -460,14 +468,7 @@ func (w *worker) handleDDLJobQueue(d *ddlCtx) error { // If running job meets error, we will save this error in job Error // and retry later if the job is not cancelled. - tidbutil.WithRecovery(func() { - schemaVer, runJobErr = w.runDDLJob(d, t, job) - }, func(r interface{}) { - if r != nil { - // If run ddl job panic, just cancel the ddl jobs. - job.State = model.JobStateCancelling - } - }) + schemaVer, runJobErr = w.runDDLJob(d, t, job) if job.IsCancelled() { txn.Discard() err = w.finishDDLJob(t, job) @@ -562,6 +563,12 @@ func chooseLeaseTime(t, max time.Duration) time.Duration { // runDDLJob runs a DDL job. It returns the current schema version in this transaction and the error. func (w *worker) runDDLJob(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + defer tidbutil.Recover(metrics.LabelDDLWorker, fmt.Sprintf("%s runDDLJob", w), + func() { + // If run DDL job panic, just cancel the DDL jobs. + job.State = model.JobStateCancelling + }, false) + // Mock for run ddl job panic. failpoint.Inject("mockPanicInRunDDLJob", func(val failpoint.Value) {}) diff --git a/ddl/index.go b/ddl/index.go index aa36b220ec053..d13019381a346 100755 --- a/ddl/index.go +++ b/ddl/index.go @@ -540,15 +540,10 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo } err = w.runReorgJob(t, reorgInfo, tbl.Meta(), d.lease, func() (addIndexErr error) { - defer func() { - r := recover() - if r != nil { - buf := util.GetStack() - logutil.BgLogger().Error("[ddl] add table index panic", zap.Any("panic", r), zap.String("stack", string(buf))) - metrics.PanicCounter.WithLabelValues(metrics.LabelDDL).Inc() + defer util.Recover(metrics.LabelDDL, "onCreateIndex", + func() { addIndexErr = errCancelledDDLJob.GenWithStack("add table `%v` index `%v` panic", tblInfo.Name, indexInfo.Name) - } - }() + }, false) return w.addTableIndex(tbl, indexInfo, reorgInfo) }) if err != nil { @@ -1192,14 +1187,9 @@ func (w *addIndexWorker) handleBackfillTask(d *ddlCtx, task *reorgIndexTask) *ad func (w *addIndexWorker) run(d *ddlCtx) { logutil.BgLogger().Info("[ddl] add index worker start", zap.Int("workerID", w.id)) defer func() { - r := recover() - if r != nil { - buf := util.GetStack() - logutil.BgLogger().Error("[ddl] add index worker panic", zap.Any("panic", r), zap.String("stack", string(buf))) - metrics.PanicCounter.WithLabelValues(metrics.LabelDDL).Inc() - } w.resultCh <- &addIndexResult{err: errReorgPanic} }() + defer util.Recover(metrics.LabelDDL, "addIndexWorker.run", nil, false) for { task, more := <-w.taskCh if !more { diff --git a/ddl/util/syncer.go b/ddl/util/syncer.go index 865c5ae42c7a7..6abbdbf5d5a18 100644 --- a/ddl/util/syncer.go +++ b/ddl/util/syncer.go @@ -28,6 +28,7 @@ import ( "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/metrics" "github.com/pingcap/tidb/owner" + tidbutil "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/logutil" "go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/clientv3/concurrency" @@ -421,6 +422,8 @@ const ( var NeededCleanTTL = int64(-60) func (s *schemaVersionSyncer) StartCleanWork() { + defer tidbutil.Recover(metrics.LabelDDLSyncer, "StartCleanWorker", nil, false) + for { select { case <-s.notifyCleanExpiredPathsCh: diff --git a/domain/domain.go b/domain/domain.go index 031f117828604..52d716f2f147c 100644 --- a/domain/domain.go +++ b/domain/domain.go @@ -15,7 +15,6 @@ package domain import ( "context" - "os" "sync" "sync/atomic" "time" @@ -407,7 +406,7 @@ func (do *Domain) ShowSlowQuery(showSlow *ast.ShowSlow) []*SlowQueryInfo { } func (do *Domain) topNSlowQueryLoop() { - defer recoverInDomain("topNSlowQueryLoop", false) + defer util.Recover(metrics.LabelDomain, "topNSlowQueryLoop", nil, false) ticker := time.NewTicker(time.Minute * 10) defer func() { ticker.Stop() @@ -442,7 +441,7 @@ func (do *Domain) infoSyncerKeeper() { defer func() { do.wg.Done() logutil.BgLogger().Info("infoSyncerKeeper exited.") - recoverInDomain("infoSyncerKeeper", false) + util.Recover(metrics.LabelDomain, "infoSyncerKeeper", nil, false) }() ticker := time.NewTicker(infosync.ReportInterval) defer ticker.Stop() @@ -463,7 +462,7 @@ func (do *Domain) infoSyncerKeeper() { } func (do *Domain) topologySyncerKeeper() { - defer recoverInDomain("topologySyncerKeeper", false) + defer util.Recover(metrics.LabelDomain, "topologySyncerKeeper", nil, false) ticker := time.NewTicker(infosync.TopologyTimeToRefresh) defer func() { ticker.Stop() @@ -491,7 +490,7 @@ func (do *Domain) topologySyncerKeeper() { } func (do *Domain) loadSchemaInLoop(lease time.Duration) { - defer recoverInDomain("loadSchemaInLoop", true) + defer util.Recover(metrics.LabelDomain, "loadSchemaInLoop", nil, true) // Lease renewal can run at any frequency. // Use lease/2 here as recommend by paper. ticker := time.NewTicker(lease / 2) @@ -842,7 +841,7 @@ func (do *Domain) LoadPrivilegeLoop(ctx sessionctx.Context) error { defer func() { do.wg.Done() logutil.BgLogger().Info("loadPrivilegeInLoop exited.") - recoverInDomain("loadPrivilegeInLoop", false) + util.Recover(metrics.LabelDomain, "loadPrivilegeInLoop", nil, false) }() var count int for { @@ -906,7 +905,7 @@ func (do *Domain) globalBindHandleWorkerLoop() { defer func() { do.wg.Done() logutil.BgLogger().Info("globalBindHandleWorkerLoop exited.") - recoverInDomain("globalBindHandleWorkerLoop", false) + util.Recover(metrics.LabelDomain, "globalBindHandleWorkerLoop", nil, false) }() bindWorkerTicker := time.NewTicker(bindinfo.Lease) defer bindWorkerTicker.Stop() @@ -935,7 +934,7 @@ func (do *Domain) handleEvolvePlanTasksLoop(ctx sessionctx.Context) { defer func() { do.wg.Done() logutil.BgLogger().Info("handleEvolvePlanTasksLoop exited.") - recoverInDomain("handleEvolvePlanTasksLoop", false) + util.Recover(metrics.LabelDomain, "handleEvolvePlanTasksLoop", nil, false) }() owner := do.newOwnerManager(bindinfo.Prompt, bindinfo.OwnerKey) for { @@ -1026,7 +1025,7 @@ func (do *Domain) newOwnerManager(prompt, ownerKey string) owner.Manager { } func (do *Domain) loadStatsWorker() { - defer recoverInDomain("loadStatsWorker", false) + defer util.Recover(metrics.LabelDomain, "loadStatsWorker", nil, false) lease := do.statsLease if lease == 0 { lease = 3 * time.Second @@ -1063,7 +1062,7 @@ func (do *Domain) loadStatsWorker() { } func (do *Domain) updateStatsWorker(ctx sessionctx.Context, owner owner.Manager) { - defer recoverInDomain("updateStatsWorker", false) + defer util.Recover(metrics.LabelDomain, "updateStatsWorker", nil, false) lease := do.statsLease deltaUpdateTicker := time.NewTicker(20 * lease) gcStatsTicker := time.NewTicker(100 * lease) @@ -1124,7 +1123,7 @@ func (do *Domain) updateStatsWorker(ctx sessionctx.Context, owner owner.Manager) } func (do *Domain) autoAnalyzeWorker(owner owner.Manager) { - defer recoverInDomain("autoAnalyzeWorker", false) + defer util.Recover(metrics.LabelDomain, "autoAnalyzeWorker", nil, false) statsHandle := do.StatsHandle() analyzeTicker := time.NewTicker(do.statsLease) defer func() { @@ -1173,22 +1172,6 @@ func (do *Domain) NotifyUpdatePrivilege(ctx sessionctx.Context) { } } -func recoverInDomain(funcName string, quit bool) { - r := recover() - if r == nil { - return - } - buf := util.GetStack() - logutil.BgLogger().Error("recover in domain failed", zap.String("funcName", funcName), - zap.Any("error", r), zap.String("buffer", string(buf))) - metrics.PanicCounter.WithLabelValues(metrics.LabelDomain).Inc() - if quit { - // Wait for metrics to be pushed. - time.Sleep(time.Second * 15) - os.Exit(1) - } -} - var ( // ErrInfoSchemaExpired returns the error that information schema is out of date. ErrInfoSchemaExpired = terror.ClassDomain.New(errno.ErrInfoSchemaExpired, errno.MySQLErrName[errno.ErrInfoSchemaExpired]) diff --git a/util/misc.go b/util/misc.go index 1abc7eb2663ef..ad52368969944 100644 --- a/util/misc.go +++ b/util/misc.go @@ -20,6 +20,7 @@ import ( "fmt" "io/ioutil" "net/http" + "os" "runtime" "strconv" "strings" @@ -32,6 +33,7 @@ import ( "github.com/pingcap/parser/mysql" "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/metrics" "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tipb/go-tipb" @@ -93,6 +95,34 @@ func WithRecovery(exec func(), recoverFn func(r interface{})) { exec() } +// Recover includes operations such as recovering, clearing,and printing information. +// It will dump current goroutine stack into log if catch any recover result. +// metricsLabel: The label of PanicCounter metrics. +// funcInfo: Some information for the panic function. +// recoverFn: Handler will be called after recover and before dump stack, passing `nil` means noop. +// quit: If this value is true, the current program exits after recovery. +func Recover(metricsLabel, funcInfo string, recoverFn func(), quit bool) { + r := recover() + if r == nil { + return + } + + if recoverFn != nil { + recoverFn() + } + logutil.BgLogger().Error("panic in the recoverable goroutine", + zap.String("label", metricsLabel), + zap.String("funcInfo", funcInfo), + zap.Reflect("r", r), + zap.String("stack", string(GetStack()))) + metrics.PanicCounter.WithLabelValues(metricsLabel).Inc() + if quit { + // Wait for metrics to be pushed. + time.Sleep(time.Second * 15) + os.Exit(1) + } +} + // CompatibleParseGCTime parses a string with `GCTimeFormat` and returns a time.Time. If `value` can't be parsed as that // format, truncate to last space and try again. This function is only useful when loading times that saved by // gc_worker. We have changed the format that gc_worker saves time (removed the last field), but when loading times it From 121c1535c82c0e615dd70dd1c585f2be2415cf80 Mon Sep 17 00:00:00 2001 From: HuaiyuXu <391585975@qq.com> Date: Mon, 18 May 2020 14:40:57 +0800 Subject: [PATCH 12/74] ddl: check too long ident for partition (#17236) --- ddl/db_partition_test.go | 11 +++++++++++ ddl/ddl_api.go | 3 +++ ddl/partition.go | 6 +++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ddl/db_partition_test.go b/ddl/db_partition_test.go index 3ca6d95f42097..1d2288ca35f00 100644 --- a/ddl/db_partition_test.go +++ b/ddl/db_partition_test.go @@ -278,6 +278,17 @@ func (s *testIntegrationSuite3) TestCreateTableWithPartition(c *C) { // Fix https://github.com/pingcap/tidb/issues/16333 tk.MustExec(`create table t35 (dt timestamp) partition by range (unix_timestamp(dt)) (partition p0 values less than (unix_timestamp('2020-04-15 00:00:00')));`) + + tk.MustExec(`drop table if exists too_long_identifier`) + tk.MustGetErrCode(`create table too_long_identifier(a int) +partition by range (a) +(partition p0pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp values less than (10));`, tmysql.ErrTooLongIdent) + + tk.MustExec(`drop table if exists too_long_identifier`) + tk.MustExec("create table too_long_identifier(a int) partition by range(a) (partition p0 values less than(10))") + tk.MustGetErrCode("alter table too_long_identifier add partition "+ + "(partition p0pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp values less than(20))", tmysql.ErrTooLongIdent) + } func (s *testIntegrationSuite2) TestCreateTableWithHashPartition(c *C) { diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index 42d973f87b995..8b95e966c01e4 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -4476,6 +4476,9 @@ func buildPartitionInfo(ctx sessionctx.Context, meta *model.TableInfo, d *ddl, s if err := def.Clause.Validate(part.Type, len(part.Columns)); err != nil { return nil, errors.Trace(err) } + if err := checkTooLongTable(def.Name); err != nil { + return nil, err + } // For RANGE partition only VALUES LESS THAN should be possible. clause := def.Clause.(*ast.PartitionDefinitionClauseLessThan) if len(part.Columns) > 0 { diff --git a/ddl/partition.go b/ddl/partition.go index 7e799a350592c..601760b234c25 100644 --- a/ddl/partition.go +++ b/ddl/partition.go @@ -133,9 +133,13 @@ func buildHashPartitionDefinitions(ctx sessionctx.Context, s *ast.CreateTableStm return nil } -func buildRangePartitionDefinitions(ctx sessionctx.Context, s *ast.CreateTableStmt, pi *model.PartitionInfo) error { +func buildRangePartitionDefinitions(ctx sessionctx.Context, s *ast.CreateTableStmt, pi *model.PartitionInfo) (err error) { for _, def := range s.Partition.Definitions { comment, _ := def.Comment() + err = checkTooLongTable(def.Name) + if err != nil { + return err + } piDef := model.PartitionDefinition{ Name: def.Name, Comment: comment, From f06fc3553187208df30e63abdc7c91f88ffbcab7 Mon Sep 17 00:00:00 2001 From: Yiding Cui Date: Mon, 18 May 2020 15:41:45 +0800 Subject: [PATCH 13/74] planner: return error when `SPLIT PARTITION TABLE` on non-partitioned table (#17218) --- executor/executor_test.go | 3 +++ planner/core/planbuilder.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/executor/executor_test.go b/executor/executor_test.go index b49b183d8ea96..f4ee07c942ec3 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -4251,6 +4251,9 @@ func (s *testSplitTable) TestShowTableRegion(c *C) { tk.MustExec("set global tidb_scatter_region = 1") atomic.StoreUint32(&ddl.EnableSplitTableRegion, 1) tk.MustExec("create table t_regions (a int key, b int, c int, index idx(b), index idx2(c))") + _, err := tk.Exec("split partition table t_regions partition (p1,p2) index idx between (0) and (20000) regions 2;") + c.Assert(err.Error(), Equals, plannercore.ErrPartitionClauseOnNonpartitioned.Error()) + // Test show table regions. tk.MustQuery(`split table t_regions between (-10000) and (10000) regions 4;`).Check(testkit.Rows("4 1")) re := tk.MustQuery("show table t_regions regions") diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 4cc682e664ace..7c8910f835bc3 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -2394,6 +2394,9 @@ func (b *PlanBuilder) buildIndexAdvise(node *ast.IndexAdviseStmt) Plan { } func (b *PlanBuilder) buildSplitRegion(node *ast.SplitRegionStmt) (Plan, error) { + if node.SplitSyntaxOpt != nil && node.SplitSyntaxOpt.HasPartition && node.Table.TableInfo.Partition == nil { + return nil, ErrPartitionClauseOnNonpartitioned + } if len(node.IndexName.L) != 0 { return b.buildSplitIndexRegion(node) } From ac30f5322e253125002663ad7c789807acfc9305 Mon Sep 17 00:00:00 2001 From: Zejun Li Date: Mon, 18 May 2020 17:28:44 +0800 Subject: [PATCH 14/74] mockstore: introducing embedded unistore (#17156) --- bindinfo/bind_test.go | 15 +- config/config.go | 2 + ddl/db_change_test.go | 2 +- ddl/db_integration_test.go | 15 +- ddl/db_test.go | 15 +- ddl/ddl_test.go | 2 +- ddl/failtest/fail_db_test.go | 15 +- ddl/serial_test.go | 15 +- ddl/table_split_test.go | 2 +- ddl/util/syncer_test.go | 2 +- domain/db_test.go | 9 +- domain/domain_test.go | 4 +- domain/global_vars_cache_test.go | 4 +- executor/analyze_test.go | 43 +-- executor/batch_point_get_test.go | 2 +- executor/chunk_size_control_test.go | 13 +- executor/executor_test.go | 35 +- executor/infoschema_reader_test.go | 2 +- executor/memory_test.go | 2 +- executor/oomtest/oom_test.go | 2 +- executor/point_get_test.go | 4 +- executor/seqtest/seq_executor_test.go | 21 +- executor/simple_test.go | 9 +- executor/update_test.go | 15 +- executor/write_test.go | 2 +- expression/integration_test.go | 4 +- go.mod | 7 +- go.sum | 71 ++-- infoschema/infoschema_test.go | 6 +- infoschema/perfschema/tables_test.go | 2 +- infoschema/tables_test.go | 2 +- meta/autoid/autoid_test.go | 16 +- meta/meta_test.go | 10 +- owner/manager_test.go | 4 +- planner/cascades/integration_test.go | 2 +- planner/core/cbo_test.go | 2 +- .../core/memtable_predicate_extractor_test.go | 2 +- privilege/privileges/cache_test.go | 4 +- privilege/privileges/privileges_test.go | 2 +- server/conn_test.go | 2 +- server/http_handler_test.go | 17 +- server/rpc_server.go | 4 + server/statistics_handler_test.go | 4 +- server/tidb_test.go | 4 +- server/util_test.go | 2 +- session/bench_test.go | 2 +- session/session_test.go | 28 +- session/tidb_test.go | 4 +- sessionctx/binloginfo/binloginfo_test.go | 2 +- statistics/handle/handle_test.go | 2 +- statistics/selectivity_test.go | 2 +- store/helper/helper_test.go | 4 +- store/mockstore/cluster/cluster.go | 2 +- store/mockstore/mockstore.go | 200 ++++++++++++ store/mockstore/mocktikv/cluster.go | 8 +- store/mockstore/mocktikv/cluster_test.go | 14 +- store/mockstore/mocktikv/executor_test.go | 15 +- store/mockstore/mocktikv/mock.go | 21 +- store/mockstore/tikv.go | 91 +----- store/mockstore/tikv_test.go | 2 +- store/mockstore/unistore.go | 31 ++ store/mockstore/unistore/cluster.go | 138 ++++++++ store/mockstore/unistore/mock.go | 68 ++++ store/mockstore/unistore/pd.go | 98 ++++++ store/mockstore/unistore/rpc.go | 309 ++++++++++++++++++ store/store_test.go | 4 +- store/tikv/2pc_test.go | 24 +- store/tikv/coprocessor_test.go | 6 +- store/tikv/delete_range_test.go | 17 +- store/tikv/gcworker/gc_worker_test.go | 26 +- store/tikv/range_task_test.go | 15 +- store/tikv/rawkv_test.go | 2 +- store/tikv/region_cache_test.go | 4 +- store/tikv/region_request_test.go | 4 +- store/tikv/split_test.go | 15 +- store/tikv/ticlient_test.go | 8 +- structure/structure_test.go | 2 +- table/tables/index_test.go | 2 +- table/tables/tables_test.go | 2 +- tidb-server/main.go | 6 +- types/const_test.go | 14 +- util/admin/admin_integration_test.go | 15 +- util/admin/admin_test.go | 2 +- util/prefix_helper_test.go | 2 +- util/profile/profile_test.go | 2 +- util/ranger/ranger_test.go | 9 +- 86 files changed, 1208 insertions(+), 433 deletions(-) create mode 100644 store/mockstore/mockstore.go create mode 100644 store/mockstore/unistore.go create mode 100644 store/mockstore/unistore/cluster.go create mode 100644 store/mockstore/unistore/mock.go create mode 100644 store/mockstore/unistore/pd.go create mode 100644 store/mockstore/unistore/rpc.go diff --git a/bindinfo/bind_test.go b/bindinfo/bind_test.go index a0c76234583f7..353d166448da4 100644 --- a/bindinfo/bind_test.go +++ b/bindinfo/bind_test.go @@ -34,7 +34,6 @@ import ( "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/stmtsummary" "github.com/pingcap/tidb/util/testkit" @@ -67,15 +66,11 @@ func (s *testSuite) SetUpSuite(c *C) { flag.Lookup("mockTikv") useMockTikv := *mockTikv if useMockTikv { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.store = store diff --git a/config/config.go b/config/config.go index 55f9b2890dd2d..6c0f1c0787d7b 100644 --- a/config/config.go +++ b/config/config.go @@ -35,6 +35,7 @@ import ( "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/util/logutil" tracing "github.com/uber/jaeger-client-go/config" + "go.uber.org/zap" ) @@ -64,6 +65,7 @@ var ( ValidStorage = map[string]bool{ "mocktikv": true, "tikv": true, + "unistore": true, } // checkTableBeforeDrop enable to execute `admin check table` before `drop table`. CheckTableBeforeDrop = false diff --git a/ddl/db_change_test.go b/ddl/db_change_test.go index 0928bf75afa85..c5b5f23c755f6 100644 --- a/ddl/db_change_test.go +++ b/ddl/db_change_test.go @@ -69,7 +69,7 @@ func (s *testStateChangeSuiteBase) SetUpSuite(c *C) { s.lease = 200 * time.Millisecond ddl.SetWaitTimeWhenErrorOccurred(1 * time.Microsecond) var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) session.SetSchemaLease(s.lease) s.dom, err = session.BootstrapSession(s.store) diff --git a/ddl/db_integration_test.go b/ddl/db_integration_test.go index c9dfb1d1f6e23..10a4373b37a9c 100644 --- a/ddl/db_integration_test.go +++ b/ddl/db_integration_test.go @@ -40,7 +40,6 @@ import ( "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/israce" @@ -71,15 +70,11 @@ func setupIntegrationSuite(s *testIntegrationSuite, c *C) { s.lease = 50 * time.Millisecond ddl.SetWaitTimeWhenErrorOccurred(0) - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + s.store, err = mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) session.SetSchemaLease(s.lease) diff --git a/ddl/db_test.go b/ddl/db_test.go index a3435a5071725..304dac85f3187 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -43,7 +43,6 @@ import ( "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/tablecodec" @@ -95,15 +94,11 @@ func setUpSuite(s *testDBSuite, c *C) { s.autoIDStep = autoid.GetStep() ddl.SetWaitTimeWhenErrorOccurred(0) - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + s.store, err = mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) diff --git a/ddl/ddl_test.go b/ddl/ddl_test.go index 03b4baaf73bbc..a857afa69ffef 100644 --- a/ddl/ddl_test.go +++ b/ddl/ddl_test.go @@ -117,7 +117,7 @@ func testNewDDLAndStart(ctx context.Context, c *C, options ...Option) *ddl { } func testCreateStore(c *C, name string) kv.Storage { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) return store } diff --git a/ddl/failtest/fail_db_test.go b/ddl/failtest/fail_db_test.go index 14c5af7d60784..83154f20fd8cf 100644 --- a/ddl/failtest/fail_db_test.go +++ b/ddl/failtest/fail_db_test.go @@ -36,7 +36,6 @@ import ( "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/testkit" "github.com/pingcap/tidb/util/testleak" @@ -66,15 +65,11 @@ func (s *testFailDBSuite) SetUpSuite(c *C) { s.lease = 200 * time.Millisecond ddl.SetWaitTimeWhenErrorOccurred(1 * time.Microsecond) var err error - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + s.store, err = mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) session.SetSchemaLease(s.lease) diff --git a/ddl/serial_test.go b/ddl/serial_test.go index 425f364ff6252..5f1491f2674d3 100644 --- a/ddl/serial_test.go +++ b/ddl/serial_test.go @@ -41,7 +41,6 @@ import ( "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/util/admin" "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/gcutil" @@ -71,16 +70,12 @@ func (s *testSerialSuite) SetUpSuite(c *C) { ddl.SetWaitTimeWhenErrorOccurred(1 * time.Microsecond) - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - var err error - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + s.store, err = mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) diff --git a/ddl/table_split_test.go b/ddl/table_split_test.go index fa3e9384b83b9..a64156a007459 100644 --- a/ddl/table_split_test.go +++ b/ddl/table_split_test.go @@ -33,7 +33,7 @@ type testDDLTableSplitSuite struct{} var _ = Suite(&testDDLTableSplitSuite{}) func (s *testDDLTableSplitSuite) TestTableSplit(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() session.SetSchemaLease(100 * time.Millisecond) diff --git a/ddl/util/syncer_test.go b/ddl/util/syncer_test.go index 528f7553683f7..11adf416d9709 100644 --- a/ddl/util/syncer_test.go +++ b/ddl/util/syncer_test.go @@ -51,7 +51,7 @@ func TestSyncerSimple(t *testing.T) { CheckVersFirstWaitTime = origin }() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { t.Fatal(err) } diff --git a/domain/db_test.go b/domain/db_test.go index 3c2caadc89347..b484c6d375486 100644 --- a/domain/db_test.go +++ b/domain/db_test.go @@ -20,7 +20,6 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" ) type dbTestSuite struct{} @@ -30,13 +29,7 @@ var _ = Suite(&dbTestSuite{}) func (ts *dbTestSuite) TestIntegration(c *C) { var err error lease := 50 * time.Millisecond - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - mvccStore := mocktikv.MustNewMVCCStore() - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), - ) + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) session.SetSchemaLease(lease) _, err = session.BootstrapSession(store) diff --git a/domain/domain_test.go b/domain/domain_test.go index 55f397daedda4..b34062302b79c 100644 --- a/domain/domain_test.go +++ b/domain/domain_test.go @@ -148,7 +148,7 @@ func TestInfo(t *testing.T) { testleak.AfterTestT(t)() }() ddlLease := 80 * time.Millisecond - s, err := mockstore.NewMockTikvStore() + s, err := mockstore.NewMockStore() if err != nil { t.Fatal(err) } @@ -293,7 +293,7 @@ func (msm *mockSessionManager) UpdateTLSConfig(cfg *tls.Config) {} func (*testSuite) TestT(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) ddlLease := 80 * time.Millisecond dom := NewDomain(store, ddlLease, 0, mockFactory) diff --git a/domain/global_vars_cache_test.go b/domain/global_vars_cache_test.go index 723d2c1c5a441..cf08f11dae2d1 100644 --- a/domain/global_vars_cache_test.go +++ b/domain/global_vars_cache_test.go @@ -39,7 +39,7 @@ func (gvcSuite *testGVCSuite) TestSimple(c *C) { defer testleak.AfterTest(c)() testleak.BeforeTest() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() ddlLease := 50 * time.Millisecond @@ -173,7 +173,7 @@ func (gvcSuite *testGVCSuite) TestCheckEnableStmtSummary(c *C) { defer testleak.AfterTest(c)() testleak.BeforeTest() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() ddlLease := 50 * time.Millisecond diff --git a/executor/analyze_test.go b/executor/analyze_test.go index 72141b4832f67..8d8eef42b8e89 100644 --- a/executor/analyze_test.go +++ b/executor/analyze_test.go @@ -35,7 +35,7 @@ import ( "github.com/pingcap/tidb/statistics" "github.com/pingcap/tidb/statistics/handle" "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" + "github.com/pingcap/tidb/store/mockstore/cluster" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/store/tikv/tikvrpc" "github.com/pingcap/tidb/table" @@ -181,10 +181,12 @@ func (s *testSuite1) TestAnalyzeTooLongColumns(c *C) { } func (s *testFastAnalyze) TestAnalyzeFastSample(c *C) { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), + var cls cluster.Cluster + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + cls = c + }), ) c.Assert(err, IsNil) defer store.Close() @@ -207,7 +209,7 @@ func (s *testFastAnalyze) TestAnalyzeFastSample(c *C) { // construct 5 regions split by {12, 24, 36, 48} splitKeys := generateTableSplitKeyForInt(tid, []int{12, 24, 36, 48}) - manipulateCluster(cluster, splitKeys) + manipulateCluster(cls, splitKeys) for i := 0; i < 60; i++ { tk.MustExec(fmt.Sprintf("insert into t values (%d, %d)", i, i)) @@ -274,10 +276,12 @@ func checkHistogram(sc *stmtctx.StatementContext, hg *statistics.Histogram) (boo } func (s *testFastAnalyze) TestFastAnalyze(c *C) { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), + var cls cluster.Cluster + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + cls = c + }), ) c.Assert(err, IsNil) defer store.Close() @@ -303,7 +307,7 @@ func (s *testFastAnalyze) TestFastAnalyze(c *C) { // construct 6 regions split by {10, 20, 30, 40, 50} splitKeys := generateTableSplitKeyForInt(tid, []int{10, 20, 30, 40, 50}) - manipulateCluster(cluster, splitKeys) + manipulateCluster(cls, splitKeys) for i := 0; i < 20; i++ { tk.MustExec(fmt.Sprintf(`insert into t values (%d, %d, "char")`, i*3, i*3)) @@ -477,14 +481,13 @@ func (s *testFastAnalyze) TestFastAnalyzeRetryRowCount(c *C) { return cli } - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithHijackClient(hijackClient), - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + var cls cluster.Cluster + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + cls = c + }), + mockstore.WithClientHijacker(hijackClient), ) c.Assert(err, IsNil) defer store.Close() @@ -505,7 +508,7 @@ func (s *testFastAnalyze) TestFastAnalyzeRetryRowCount(c *C) { for i := 0; i < 30; i++ { tk.MustExec(fmt.Sprintf("insert into retry_row_count values (%d)", i)) } - cluster.SplitTable(tid, 6) + cls.SplitTable(tid, 6) // Flush the region cache first. tk.MustQuery("select * from retry_row_count") tk.MustExec("analyze table retry_row_count") diff --git a/executor/batch_point_get_test.go b/executor/batch_point_get_test.go index 3374017d21e40..cd53b8093b523 100644 --- a/executor/batch_point_get_test.go +++ b/executor/batch_point_get_test.go @@ -29,7 +29,7 @@ type testBatchPointGetSuite struct { } func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return nil, nil, errors.Trace(err) } diff --git a/executor/chunk_size_control_test.go b/executor/chunk_size_control_test.go index 2fbfeea0788b5..bd21a631fdff4 100644 --- a/executor/chunk_size_control_test.go +++ b/executor/chunk_size_control_test.go @@ -27,7 +27,6 @@ import ( "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/store/tikv/tikvrpc" "github.com/pingcap/tidb/tablecodec" @@ -133,14 +132,14 @@ func (s *testChunkSizeControlSuite) SetUpSuite(c *C) { kit := new(testChunkSizeControlKit) s.m[name] = kit kit.client = &testSlowClient{regionDelay: make(map[uint64]time.Duration)} - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - kit.cluster = cluster var err error - kit.store, err = mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithHijackClient(func(c tikv.Client) tikv.Client { + kit.store, err = mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + kit.cluster = c + }), + mockstore.WithClientHijacker(func(c tikv.Client) tikv.Client { kit.client.Client = c return kit.client }), diff --git a/executor/executor_test.go b/executor/executor_test.go index f4ee07c942ec3..b21dd1ac78a5c 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -56,7 +56,6 @@ import ( "github.com/pingcap/tidb/statistics" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/store/tikv/oracle" "github.com/pingcap/tidb/store/tikv/tikvrpc" @@ -147,15 +146,11 @@ func (s *baseTestSuite) SetUpSuite(c *C) { flag.Lookup("mockTikv") useMockTikv := *mockTikv if useMockTikv { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.store = store @@ -2887,8 +2882,8 @@ func (s *testSuiteWithCliBase) SetUpSuite(c *C) { s.cli = cli var err error - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithHijackClient(hijackClient), + s.store, err = mockstore.NewMockStore( + mockstore.WithClientHijacker(hijackClient), ) c.Assert(err, IsNil) session.SetStatsLease(0) @@ -2919,8 +2914,8 @@ func (s *testSuite2) TestAddIndexPriority(c *C) { return cli } - store, err := mockstore.NewMockTikvStore( - mockstore.WithHijackClient(hijackClient), + store, err := mockstore.NewMockStore( + mockstore.WithClientHijacker(hijackClient), ) c.Assert(err, IsNil) dom, err := session.BootstrapSession(store) @@ -4627,12 +4622,12 @@ func (s *testRecoverTable) SetUpSuite(c *C) { s.cli = cli var err error - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithHijackClient(hijackClient), - mockstore.WithCluster(cluster), + s.store, err = mockstore.NewMockStore( + mockstore.WithClientHijacker(hijackClient), + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.dom, err = session.BootstrapSession(s.store) diff --git a/executor/infoschema_reader_test.go b/executor/infoschema_reader_test.go index 51eb2e165a31f..de25cb5befee9 100644 --- a/executor/infoschema_reader_test.go +++ b/executor/infoschema_reader_test.go @@ -97,7 +97,7 @@ func (s *inspectionSuite) SetUpSuite(c *C) { testleak.BeforeTest() var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) session.DisableStats4Test() s.dom, err = session.BootstrapSession(s.store) diff --git a/executor/memory_test.go b/executor/memory_test.go index d287fe9fbffff..143a24fe42942 100644 --- a/executor/memory_test.go +++ b/executor/memory_test.go @@ -34,7 +34,7 @@ type testMemoryLeak struct { func (s *testMemoryLeak) SetUpSuite(c *C) { var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) s.domain, err = session.BootstrapSession(s.store) c.Assert(err, IsNil) diff --git a/executor/oomtest/oom_test.go b/executor/oomtest/oom_test.go index 9f2e28e3efa1d..4f0d6a301a250 100644 --- a/executor/oomtest/oom_test.go +++ b/executor/oomtest/oom_test.go @@ -45,7 +45,7 @@ func (s *testOOMSuite) SetUpSuite(c *C) { testleak.BeforeTest() s.registerHook() var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) domain.RunAutoAnalyze = false s.do, err = session.BootstrapSession(s.store) diff --git a/executor/point_get_test.go b/executor/point_get_test.go index c94da7dde4c2a..7bb270ebdbff7 100644 --- a/executor/point_get_test.go +++ b/executor/point_get_test.go @@ -47,8 +47,8 @@ func (s *testPointGetSuite) SetUpSuite(c *C) { s.cli = cli var err error - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithHijackClient(hijackClient), + s.store, err = mockstore.NewMockStore( + mockstore.WithClientHijacker(hijackClient), ) c.Assert(err, IsNil) s.dom, err = session.BootstrapSession(s.store) diff --git a/executor/seqtest/seq_executor_test.go b/executor/seqtest/seq_executor_test.go index 371be9894178e..8d936390e196c 100644 --- a/executor/seqtest/seq_executor_test.go +++ b/executor/seqtest/seq_executor_test.go @@ -52,7 +52,6 @@ import ( "github.com/pingcap/tidb/statistics/handle" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/store/tikv/tikvrpc" "github.com/pingcap/tidb/util/collate" @@ -88,18 +87,14 @@ func (s *seqTestSuite) SetUpSuite(c *C) { flag.Lookup("mockTikv") useMockTikv := *mockTikv if useMockTikv { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + var err error + s.store, err = mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) - s.store = store session.SetSchemaLease(0) session.DisableStats4Test() } @@ -1134,8 +1129,8 @@ func (s *seqTestSuite1) SetUpSuite(c *C) { s.cli = cli var err error - s.store, err = mockstore.NewMockTikvStore( - mockstore.WithHijackClient(hijackClient), + s.store, err = mockstore.NewMockStore( + mockstore.WithClientHijacker(hijackClient), ) c.Assert(err, IsNil) s.dom, err = session.BootstrapSession(s.store) diff --git a/executor/simple_test.go b/executor/simple_test.go index 5db1950220c00..092b009f2eac4 100644 --- a/executor/simple_test.go +++ b/executor/simple_test.go @@ -29,7 +29,6 @@ import ( "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/util/testkit" "github.com/pingcap/tidb/util/testutil" ) @@ -528,13 +527,7 @@ type testFlushSuite struct{} func (s *testFlushSuite) TestFlushPrivilegesPanic(c *C) { // Run in a separate suite because this test need to set SkipGrantTable config. - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - mvccStore := mocktikv.MustNewMVCCStore() - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), - ) + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() diff --git a/executor/update_test.go b/executor/update_test.go index 87ef2ffb11d99..e5e0e4f92a57c 100644 --- a/executor/update_test.go +++ b/executor/update_test.go @@ -24,7 +24,6 @@ import ( "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/util/mock" "github.com/pingcap/tidb/util/testkit" ) @@ -42,15 +41,11 @@ func (s *testUpdateSuite) SetUpSuite(c *C) { flag.Lookup("mockTikv") useMockTikv := *mockTikv if useMockTikv { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.store = store diff --git a/executor/write_test.go b/executor/write_test.go index 757bf1350b137..96634206afb5f 100644 --- a/executor/write_test.go +++ b/executor/write_test.go @@ -2304,7 +2304,7 @@ func (s *testSuite4) TestNotNullDefault(c *C) { } func (s *testBypassSuite) TestLatch(c *C) { - store, err := mockstore.NewMockTikvStore( + store, err := mockstore.NewMockStore( // Small latch slot size to make conflicts. mockstore.WithTxnLocalLatches(64), ) diff --git a/expression/integration_test.go b/expression/integration_test.go index 9a64ecff79446..c363106f7ff71 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -580,7 +580,7 @@ func (s *testIntegrationSuite2) TestMathBuiltin(c *C) { tk.MustExec("create table t(a int)") tk.MustExec("insert into t values(1),(2),(3)") tk.Se.GetSessionVars().MaxChunkSize = 1 - tk.MustQuery("select rand(1) from t").Check(testkit.Rows("0.40540353712197724", "0.8716141803857071", "0.1418603212962489")) + tk.MustQuery("select rand(1) from t").Sort().Check(testkit.Rows("0.1418603212962489", "0.40540353712197724", "0.8716141803857071")) tk.MustQuery("select rand(a) from t").Check(testkit.Rows("0.40540353712197724", "0.6555866465490187", "0.9057697559760601")) tk.MustQuery("select rand(1), rand(2), rand(3)").Check(testkit.Rows("0.40540353712197724 0.6555866465490187 0.9057697559760601")) } @@ -4420,7 +4420,7 @@ func (s *testIntegrationSuite) TestTiDBInternalFunc(c *C) { } func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return nil, nil, err } diff --git a/go.mod b/go.mod index 4ab845476a19b..33373f811296d 100644 --- a/go.mod +++ b/go.mod @@ -17,9 +17,10 @@ require ( github.com/google/uuid v1.1.1 github.com/gorilla/mux v1.7.3 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 - github.com/klauspost/cpuid v1.2.0 + github.com/klauspost/cpuid v1.2.1 github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7 github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef + github.com/ngaut/unistore v0.0.0-20200515074342-406feebf3ca9 github.com/opentracing/basictracer-go v1.0.0 github.com/opentracing/opentracing-go v1.1.0 github.com/pingcap/br v0.0.0-20200426093517-dd11ae28b885 @@ -29,7 +30,7 @@ require ( github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677 - github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd + github.com/pingcap/log v0.0.0-20200511115504-543df19646ad github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657 github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 @@ -45,7 +46,6 @@ require ( github.com/tiancaiamao/appdash v0.0.0-20181126055449-889f96f722a2 github.com/uber-go/atomic v1.3.2 github.com/uber/jaeger-client-go v2.22.1+incompatible - github.com/uber/jaeger-lib v2.2.0+incompatible // indirect go.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738 go.uber.org/atomic v1.6.0 go.uber.org/automaxprocs v1.2.0 @@ -57,6 +57,7 @@ require ( golang.org/x/tools v0.0.0-20200325203130-f53864d0dba1 google.golang.org/grpc v1.25.1 gopkg.in/natefinch/lumberjack.v2 v2.0.0 + honnef.co/go/tools v0.0.1-2020.1.4 // indirect sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 ) diff --git a/go.sum b/go.sum index 85d08c7650c41..9eed90567acbb 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,9 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/aws/aws-sdk-go v1.26.1 h1:JGQggXhOiNJIqsmbYUl3cYtJZUffeOWlHtxfzGK7WPI= github.com/aws/aws-sdk-go v1.26.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blacktear23/go-proxyprotocol v0.0.0-20180807104634-af7a81e8dd0d h1:rQlvB2AYWme2bIB18r/SipGiMEVJYE9U0z+MGoU/LtQ= github.com/blacktear23/go-proxyprotocol v0.0.0-20180807104634-af7a81e8dd0d/go.mod h1:VKt7CNAQxpFpSDz3sXyj9hY/GbVsQCr0sB3w59nE7lU= @@ -56,14 +57,18 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coocood/badger v1.5.1-0.20200515070411-e02af0688441 h1:kz8q4CQFnlNynLwnMPxIChtQybSTX1p7GNxa3//MfsM= +github.com/coocood/badger v1.5.1-0.20200515070411-e02af0688441/go.mod h1:klY8SfH2lNZ/23/SIxwHoJw+T6wYGB12YPCF9MUoiu0= +github.com/coocood/bbloom v0.0.0-20190830030839-58deb6228d64 h1:W1SHiII3e0jVwvaQFglwu3kS9NLxOeTpvik7MbKCyuQ= +github.com/coocood/bbloom v0.0.0-20190830030839-58deb6228d64/go.mod h1:F86k/6c7aDUdwSUevnLpHS/3Q9hzYCE99jGk2xsHnt0= +github.com/coocood/rtutil v0.0.0-20190304133409-c84515f646f2 h1:NnLfQ77q0G4k2Of2c1ceQ0ec6MkLQyDp+IGdVM0D8XM= +github.com/coocood/rtutil v0.0.0-20190304133409-c84515f646f2/go.mod h1:7qG7YFnOALvsx6tKTNmQot8d7cGFXM9TidzvRFLWYwM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 h1:3jFq2xL4ZajGK4aZY8jz+DAF0FHjI51BXjjSwCzS1Dk= github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -91,6 +96,7 @@ github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+ github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= @@ -99,9 +105,7 @@ github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:Htrtb github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -109,7 +113,6 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -156,7 +159,6 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -176,7 +178,6 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -212,9 +213,7 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.12.1 h1:zCy2xE9ablevUOrUZc3Dl72Dt+ya2FNAvC2yLYMHzi4= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= @@ -237,23 +236,35 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= +github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9 h1:Y+lzErDTURqeXqlqYi4YBYbDd7ycU74gW1ADt57/bgY= +github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/ratelimit v1.0.1 h1:+7AIFJVQ0EQgq/K9+0Krm7m530Du7tIz0METWzN0RgY= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= +github.com/juju/retry v0.0.0-20160928201858-1998d01ba1c3/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= +github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0 h1:+WWUkhnTjV6RNOxkcwk79qrjeyHEHvBzlneueBsatX4= +github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0/go.mod h1:hpGvhGHPVbNBraRLZEhoQwFLMrjK8PSlO4D3nDjKYXo= +github.com/juju/utils v0.0.0-20180808125547-9dfc6dbfb02b/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= +github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/go-bindata v3.18.0+incompatible/go.mod h1:/pEEZ72flUW2p0yi30bslSp9YqD9pysLxunQDdb2CPM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDKIzp7y4voR9CX/nvcfymLmg2UiOio= +github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -293,7 +304,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.0.2/go.mod h1:rb0dQy1LVAxW9SWy5R3LPUjevzUbUS316U5MFySA2lo= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -302,31 +312,31 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/montanaflynn/stats v0.0.0-20151014174947-eeaced052adb/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808 h1:pmpDGKLw4n82EtrNiLqB+xSz/JQwFOaZuMALYUHwX5s= github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.5.0 h1:2EkzeTSqBB4V4bJwWrt5gIIrZmpJBcoIRGS2kWLgzmk= github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/ncw/directio v1.0.4 h1:CojwI07mCEmRkajgx42Pf8jyCwTs1ji9/Ij9/PJG12k= +github.com/ncw/directio v1.0.4/go.mod h1:CKGdcN7StAaqjT7Qack3lAXeX4pjnyc46YeqZH1yWVY= github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7 h1:7KAv7KMGTTqSmYZtNdcNTgsos+vFzULLwyElndwn+5c= github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7/go.mod h1:iWMfgwqYW+e8n5lC/jjNEhwcjbRDpl5NT7n2h+4UNcI= github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef h1:K0Fn+DoFqNqktdZtdV3bPQ/0cuYh2H4rkg0tytX/07k= github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef/go.mod h1:7WjlapSfwQyo6LNmIvEWzsW1hbBQfpUO4JWnuQRmva8= +github.com/ngaut/unistore v0.0.0-20200515074342-406feebf3ca9 h1:3jyYAfLtjYY1CMVzE5U//Wd6Qm8uPa2sCkH5nA34qKE= +github.com/ngaut/unistore v0.0.0-20200515074342-406feebf3ca9/go.mod h1:wU2ZTg5G6OToAvCdj1uXa9LxXxna033vfGfIgU4klwY= github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= -github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -334,6 +344,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.3.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d h1:U+PMnTlV2tu7RuMK5etusZG3Cf+rpow5hqQByeCzJ2g= github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d/go.mod h1:lXfE4PvvTW5xOjO6Mba8zDPyw8M93B6AQ7frTGnMlA8= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap-incubator/tidb-dashboard v0.0.0-20200407064406-b2b8ad403d01/go.mod h1:77fCh8d3oKzC5ceOJWeZXAS/mLzVgdZ7rKniwmOyFuo= github.com/pingcap/br v0.0.0-20200426093517-dd11ae28b885 h1:gI14HoGBF9UyECMgqSRZx1ONhREtrZe8JCmZ/6OFilw= github.com/pingcap/br v0.0.0-20200426093517-dd11ae28b885/go.mod h1:4w3meMnk7HDNpNgjuRAxavruTeKJvUiXxoEWTjzXPnA= @@ -348,7 +360,6 @@ github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTw github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011 h1:58naV4XMEqm0hl9LcYo6cZoGBGiLtefMQMF/vo3XLgQ= github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pingcap/failpoint v0.0.0-20191029060244-12f4ac2fd11d h1:F8vp38kTAckN+v8Jlc98uMBvKIzr1a+UhnLyVYn8Q5Q= github.com/pingcap/failpoint v0.0.0-20191029060244-12f4ac2fd11d/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI= github.com/pingcap/failpoint v0.0.0-20200210140405-f8f9fb234798 h1:6DMbRqPI1qzQ8N1xc3+nKY8IxSACd9VqQKkRVvbyoIg= github.com/pingcap/failpoint v0.0.0-20200210140405-f8f9fb234798/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI= @@ -356,6 +367,7 @@ github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d h1:rCmRK0lCRrHMUbS99BKF github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d/go.mod h1:fMRU1BA1y+r89AxUoaAar4JjrhUkVDt0o0Np6V8XbDQ= github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 h1:surzm05a8C9dN8dIUmo4Be2+pMRb6f55i+UIYrluu2E= github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw= +github.com/pingcap/kvproto v0.0.0-20190227013052-e71ca0165a5f/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY= github.com/pingcap/kvproto v0.0.0-20191211054548-3c6b38ea5107/go.mod h1:WWLmULLO7l8IOcQG+t+ItJ3fEcrL5FxF0Wu+HrMy26w= github.com/pingcap/kvproto v0.0.0-20200214064158-62d31900d88e/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= github.com/pingcap/kvproto v0.0.0-20200417092353-efbe03bcffbd/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= @@ -364,8 +376,9 @@ github.com/pingcap/kvproto v0.0.0-20200423020121-038e31959c2a/go.mod h1:IOdRDPLy github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677 h1:90pbLYmkk7bXLgyaYZj22QQLouVxqTZrswi+7DNMSRQ= github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= -github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd h1:CV3VsP3Z02MVtdpTMfEgRJ4T9NGgGTxdHpJerent7rM= github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= +github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc= +github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/parser v0.0.0-20200424075042-8222d8b724a4/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657 h1:2ceTso30kmgMeddZ4iZ6zrK8N9eFF8zmCa1hSSE1tXc= github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= @@ -375,6 +388,7 @@ github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NM github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 h1:PI8YpTl45F8ilNkrPtT4IdbcZB1SCEa+gK/U5GJYl3E= github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= github.com/pingcap/tidb v1.1.0-beta.0.20200424154252-5ede18f10eed/go.mod h1:m2VDlJDbUeHPCXAfKPajqLmB1uLvWpkKk3zALNqDYdw= +github.com/pingcap/tidb v1.1.0-beta.0.20200513065557-5a0787dfa915/go.mod h1:khS9Z9YlbtxsaZsSsSahelgh5L16EtP30QADFmPiI/I= github.com/pingcap/tidb-tools v4.0.0-beta.1.0.20200306084441-875bd09aa3d5+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible h1:+K5bqDYG5HT+GqLdx4GH5VmS84+xHgpHbGg6Xt6qQec= github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= @@ -387,6 +401,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -394,6 +409,7 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -408,6 +424,7 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44 h1:tB9NOR21++IjLyVx3/PCPhWMwqGNCMQEH96A6dMZ/gc= github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v2.19.10+incompatible h1:lA4Pi29JEVIQIgATSeftHSY0rMGI9CLrl2ZvDLiahto= github.com/shirou/gopsutil v2.19.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= @@ -416,8 +433,9 @@ github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca h1:3fECS8atRjByijiI8yYiuwLwQ2ZxXobW7ua/8GRB3pI= github.com/shurcooL/vfsgen v0.0.0-20181020040650-a97a25d856ca/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd h1:ug7PpSOB5RBPK1Kg6qskGBoP3Vnj/aNYFTznWvlkGo0= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -488,6 +506,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Ya9AIoYBpE= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zhangjinpeng1987/raft v0.0.0-20190624145930-deeb32d6553d h1:rGkexfPDxNuTCObUwTbsRUlti+evR/Ksb4dKy6esXW0= +github.com/zhangjinpeng1987/raft v0.0.0-20190624145930-deeb32d6553d/go.mod h1:1KDQ09J8MRHEtHze4at7BJZDW/doUAgkJ8w9KjEUhSo= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -520,6 +540,7 @@ go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -556,6 +577,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -607,6 +629,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -694,6 +717,7 @@ gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -705,11 +729,14 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8 gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4 h1:hILp2hNrRnYjZpmIbx70psAHbBSEcQ1NIzDcUbJ1b6g= +gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -725,6 +752,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= diff --git a/infoschema/infoschema_test.go b/infoschema/infoschema_test.go index fb84d6835fa0a..ecf6db2ec7e3a 100644 --- a/infoschema/infoschema_test.go +++ b/infoschema/infoschema_test.go @@ -44,7 +44,7 @@ type testSuite struct { func (*testSuite) TestT(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() // Make sure it calls perfschema.Init(). @@ -255,7 +255,7 @@ func (testSuite) TestConcurrent(c *C) { storeCount := 5 stores := make([]kv.Storage, storeCount) for i := 0; i < storeCount; i++ { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) stores[i] = store } @@ -278,7 +278,7 @@ func (testSuite) TestConcurrent(c *C) { // TestInfoTables makes sure that all tables of information_schema could be found in infoschema handle. func (*testSuite) TestInfoTables(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() handle := infoschema.NewHandle(store) diff --git a/infoschema/perfschema/tables_test.go b/infoschema/perfschema/tables_test.go index c960290072e62..0684d21cfb3c7 100644 --- a/infoschema/perfschema/tables_test.go +++ b/infoschema/perfschema/tables_test.go @@ -53,7 +53,7 @@ func (s *testTableSuite) SetUpSuite(c *C) { testleak.BeforeTest() var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) session.DisableStats4Test() s.dom, err = session.BootstrapSession(s.store) diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index 4d06fa4a79fcf..08d453619192f 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -65,7 +65,7 @@ func (s *testTableSuiteBase) SetUpSuite(c *C) { testleak.BeforeTest() var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) session.DisableStats4Test() s.dom, err = session.BootstrapSession(s.store) diff --git a/meta/autoid/autoid_test.go b/meta/autoid/autoid_test.go index 357dbe59efa32..91c93f574a2ec 100644 --- a/meta/autoid/autoid_test.go +++ b/meta/autoid/autoid_test.go @@ -47,7 +47,7 @@ func (*testSuite) TestT(c *C) { c.Assert(failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange"), IsNil) }() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() @@ -250,7 +250,7 @@ func (*testSuite) TestUnsignedAutoid(c *C) { c.Assert(failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange"), IsNil) }() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() @@ -407,7 +407,7 @@ func (*testSuite) TestUnsignedAutoid(c *C) { // TestConcurrentAlloc is used for the test that // multiple allocators allocate ID with the same table ID concurrently. func (*testSuite) TestConcurrentAlloc(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() autoid.SetStep(100) @@ -494,7 +494,7 @@ func (*testSuite) TestConcurrentAlloc(c *C) { // TestRollbackAlloc tests that when the allocation transaction commit failed, // the local variable base and end doesn't change. func (*testSuite) TestRollbackAlloc(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() dbID := int64(1) @@ -536,7 +536,7 @@ func (*testSuite) TestNextStep(c *C) { func BenchmarkAllocator_Alloc(b *testing.B) { b.StopTimer() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return } @@ -567,7 +567,7 @@ func BenchmarkAllocator_Alloc(b *testing.B) { func BenchmarkAllocator_SequenceAlloc(b *testing.B) { b.StopTimer() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return } @@ -622,7 +622,7 @@ func BenchmarkAllocator_Seek(b *testing.B) { } func (*testSuite) TestSequenceAutoid(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() @@ -743,7 +743,7 @@ func (*testSuite) TestSequenceAutoid(c *C) { } func (*testSuite) TestConcurrentAllocSequence(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() diff --git a/meta/meta_test.go b/meta/meta_test.go index 0cb0f6ccb42db..a26cadd8fcfdf 100644 --- a/meta/meta_test.go +++ b/meta/meta_test.go @@ -40,7 +40,7 @@ type testSuite struct { func (s *testSuite) TestMeta(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() @@ -268,7 +268,7 @@ func (s *testSuite) TestMeta(c *C) { func (s *testSuite) TestSnapshot(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() @@ -299,7 +299,7 @@ func (s *testSuite) TestSnapshot(c *C) { func (s *testSuite) TestDDL(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() @@ -443,7 +443,7 @@ func (s *testSuite) TestDDL(c *C) { func (s *testSuite) BenchmarkGenGlobalIDs(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() @@ -464,7 +464,7 @@ func (s *testSuite) BenchmarkGenGlobalIDs(c *C) { func (s *testSuite) BenchmarkGenGlobalIDOneByOne(c *C) { defer testleak.AfterTest(c)() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() diff --git a/owner/manager_test.go b/owner/manager_test.go index 7b68b02a69512..63c991011e906 100644 --- a/owner/manager_test.go +++ b/owner/manager_test.go @@ -51,7 +51,7 @@ func checkOwner(d DDL, fbVal bool) (isOwner bool) { // Ignore this test on the windows platform, because calling unix socket with address in // host:port format fails on windows. func TestSingle(t *testing.T) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { t.Fatal(err) } @@ -112,7 +112,7 @@ func TestCluster(t *testing.T) { defer func() { owner.ManagerSessionTTL = orignalTTL }() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { t.Fatal(err) } diff --git a/planner/cascades/integration_test.go b/planner/cascades/integration_test.go index 774030aa8c019..5c4c6f6cb5ac4 100644 --- a/planner/cascades/integration_test.go +++ b/planner/cascades/integration_test.go @@ -33,7 +33,7 @@ type testIntegrationSuite struct { } func newStoreWithBootstrap() (kv.Storage, error) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return nil, err } diff --git a/planner/core/cbo_test.go b/planner/core/cbo_test.go index 5a84c2a0ad2de..32645c8ef024b 100644 --- a/planner/core/cbo_test.go +++ b/planner/core/cbo_test.go @@ -558,7 +558,7 @@ func (s *testAnalyzeSuite) TestInconsistentEstimation(c *C) { } func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return nil, nil, errors.Trace(err) } diff --git a/planner/core/memtable_predicate_extractor_test.go b/planner/core/memtable_predicate_extractor_test.go index 8f176d99d4218..48d59d047476f 100644 --- a/planner/core/memtable_predicate_extractor_test.go +++ b/planner/core/memtable_predicate_extractor_test.go @@ -38,7 +38,7 @@ type extractorSuite struct { } func (s *extractorSuite) SetUpSuite(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) c.Assert(store, NotNil) diff --git a/privilege/privileges/cache_test.go b/privilege/privileges/cache_test.go index ce0e68f2b3976..fbf5b56497042 100644 --- a/privilege/privileges/cache_test.go +++ b/privilege/privileges/cache_test.go @@ -34,7 +34,7 @@ type testCacheSuite struct { } func (s *testCacheSuite) SetUpSuite(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() session.SetSchemaLease(0) session.DisableStats4Test() c.Assert(err, IsNil) @@ -354,7 +354,7 @@ func (s *testCacheSuite) TestRoleGraphBFS(c *C) { } func (s *testCacheSuite) TestAbnormalMySQLTable(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) defer store.Close() session.SetSchemaLease(0) diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index 0117db286141d..1234a1d7143ff 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -1056,7 +1056,7 @@ func mustExec(c *C, se session.Session, sql string) { } func newStore(c *C, dbPath string) (*domain.Domain, kv.Storage) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() session.SetSchemaLease(0) session.DisableStats4Test() c.Assert(err, IsNil) diff --git a/server/conn_test.go b/server/conn_test.go index 48d208669f99e..d9259b17fb99a 100644 --- a/server/conn_test.go +++ b/server/conn_test.go @@ -45,7 +45,7 @@ var _ = Suite(&ConnTestSuite{}) func (ts *ConnTestSuite) SetUpSuite(c *C) { testleak.BeforeTest() var err error - ts.store, err = mockstore.NewMockTikvStore() + ts.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) ts.dom, err = session.BootstrapSession(ts.store) c.Assert(err, IsNil) diff --git a/server/http_handler_test.go b/server/http_handler_test.go index 74f383135f9bf..0b1143d765f29 100644 --- a/server/http_handler_test.go +++ b/server/http_handler_test.go @@ -35,6 +35,7 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" zaplog "github.com/pingcap/log" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" @@ -50,7 +51,6 @@ import ( "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/store/helper" "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/tablecodec" "github.com/pingcap/tidb/types" @@ -340,9 +340,8 @@ func (ts *HTTPHandlerTestSuite) TestRegionsFromMeta(c *C) { } func (ts *HTTPHandlerTestSuite) startServer(c *C) { - mvccStore := mocktikv.MustNewMVCCStore() var err error - ts.store, err = mockstore.NewMockTikvStore(mockstore.WithMVCCStore(mvccStore)) + ts.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) ts.domain, err = session.BootstrapSession(ts.store) c.Assert(err, IsNil) @@ -443,7 +442,17 @@ func (ts *HTTPHandlerTestSuite) TestGetTableMVCC(c *C) { info := data.Value.Info c.Assert(info, NotNil) c.Assert(len(info.Writes), Greater, 0) - startTs := info.Writes[2].StartTs + + // TODO: Unistore will not return Op_Lock. + // Use this workaround to support two backend, we can remove this hack after deprecated mocktikv. + var startTs uint64 + for _, w := range info.Writes { + if w.Type == kvrpcpb.Op_Lock { + continue + } + startTs = w.StartTs + break + } resp, err = ts.fetchStatus(fmt.Sprintf("/mvcc/txn/%d/tidb/test", startTs)) c.Assert(err, IsNil) diff --git a/server/rpc_server.go b/server/rpc_server.go index 1a828074c7c28..a3b3c15f02f67 100644 --- a/server/rpc_server.go +++ b/server/rpc_server.go @@ -28,6 +28,7 @@ import ( "github.com/pingcap/tidb/privilege/privileges" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore/mocktikv" + "github.com/pingcap/tidb/store/mockstore/unistore" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/logutil" @@ -56,6 +57,9 @@ func NewRPCServer(config *config.Config, dom *domain.Domain, sm util.SessionMana mocktikv.GRPCClientFactory = func() mocktikv.Client { return tikv.NewTestRPCClient(config.Security) } + unistore.GRPCClientFactory = func() unistore.Client { + return tikv.NewTestRPCClient(config.Security) + } diagnosticspb.RegisterDiagnosticsServer(s, rpcSrv) tikvpb.RegisterTikvServer(s, rpcSrv) return s diff --git a/server/statistics_handler_test.go b/server/statistics_handler_test.go index ecaa2f70b32d3..690433e32cb41 100644 --- a/server/statistics_handler_test.go +++ b/server/statistics_handler_test.go @@ -29,7 +29,6 @@ import ( "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/statistics/handle" "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" ) type testDumpStatsSuite struct { @@ -45,9 +44,8 @@ var _ = Suite(&testDumpStatsSuite{ }) func (ds *testDumpStatsSuite) startServer(c *C) { - mvccStore := mocktikv.MustNewMVCCStore() var err error - ds.store, err = mockstore.NewMockTikvStore(mockstore.WithMVCCStore(mvccStore)) + ds.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) session.DisableStats4Test() ds.domain, err = session.BootstrapSession(ds.store) diff --git a/server/tidb_test.go b/server/tidb_test.go index 0b35f5821d982..b37c90f8f638f 100644 --- a/server/tidb_test.go +++ b/server/tidb_test.go @@ -76,7 +76,7 @@ func (ts *tidbTestSuite) SetUpSuite(c *C) { func (ts *tidbTestSuiteBase) SetUpSuite(c *C) { var err error - ts.store, err = mockstore.NewMockTikvStore() + ts.store, err = mockstore.NewMockStore() session.DisableStats4Test() c.Assert(err, IsNil) ts.domain, err = session.BootstrapSession(ts.store) @@ -183,7 +183,7 @@ func (ts *tidbTestSuite) TestStatusAPI(c *C) { func (ts *tidbTestSuite) TestStatusPort(c *C) { var err error - ts.store, err = mockstore.NewMockTikvStore() + ts.store, err = mockstore.NewMockStore() session.DisableStats4Test() c.Assert(err, IsNil) ts.domain, err = session.BootstrapSession(ts.store) diff --git a/server/util_test.go b/server/util_test.go index e228348ec6534..884f7e0ad03c8 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -33,7 +33,7 @@ import ( var _ = Suite(&testUtilSuite{}) func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return nil, nil, errors.Trace(err) } diff --git a/session/bench_test.go b/session/bench_test.go index 064e1fca49b97..ccee3f316dc1b 100644 --- a/session/bench_test.go +++ b/session/bench_test.go @@ -35,7 +35,7 @@ var smallCount = 100 var bigCount = 10000 func prepareBenchSession() (Session, *domain.Domain, kv.Storage) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { logutil.BgLogger().Fatal(err.Error()) } diff --git a/session/session_test.go b/session/session_test.go index 55d3e8a95db2f..a5d2fddc762cb 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -173,15 +173,11 @@ func (s *testSessionSuiteBase) SetUpSuite(c *C) { session.ResetStoreForWithTiKVTest(store) s.store = store } else { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.store = store @@ -1941,15 +1937,11 @@ func (s *testSchemaSuiteBase) TearDownTest(c *C) { func (s *testSchemaSuiteBase) SetUpSuite(c *C) { testleak.BeforeTest() - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.store = store diff --git a/session/tidb_test.go b/session/tidb_test.go index 7a77003d11e26..ea579f4bf0267 100644 --- a/session/tidb_test.go +++ b/session/tidb_test.go @@ -104,13 +104,13 @@ func (s *testMainSuite) TestParseErrorWarn(c *C) { } func newStore(c *C, dbPath string) kv.Storage { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) return store } func newStoreWithBootstrap(c *C, dbPath string) (kv.Storage, *domain.Domain) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) dom, err := BootstrapSession(store) c.Assert(err, IsNil) diff --git a/sessionctx/binloginfo/binloginfo_test.go b/sessionctx/binloginfo/binloginfo_test.go index 001009c378949..526d280ec9243 100644 --- a/sessionctx/binloginfo/binloginfo_test.go +++ b/sessionctx/binloginfo/binloginfo_test.go @@ -93,7 +93,7 @@ type testBinlogSuite struct { const maxRecvMsgSize = 64 * 1024 func (s *testBinlogSuite) SetUpSuite(c *C) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) s.store = store session.SetSchemaLease(0) diff --git a/statistics/handle/handle_test.go b/statistics/handle/handle_test.go index cd71900e67ac3..73e16840124de 100644 --- a/statistics/handle/handle_test.go +++ b/statistics/handle/handle_test.go @@ -464,7 +464,7 @@ func (s *testStatsSuite) TestLoadStats(c *C) { } func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return nil, nil, errors.Trace(err) } diff --git a/statistics/selectivity_test.go b/statistics/selectivity_test.go index cabeb98d06273..234702fef016a 100644 --- a/statistics/selectivity_test.go +++ b/statistics/selectivity_test.go @@ -122,7 +122,7 @@ func (h *logHook) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.Chec } func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) { - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() if err != nil { return nil, nil, errors.Trace(err) } diff --git a/store/helper/helper_test.go b/store/helper/helper_test.go index 232add28b084c..721d78cbabaed 100644 --- a/store/helper/helper_test.go +++ b/store/helper/helper_test.go @@ -27,7 +27,6 @@ import ( "github.com/pingcap/parser/model" "github.com/pingcap/tidb/store/helper" "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/util/pdapi" "go.uber.org/zap" @@ -64,8 +63,7 @@ func (s *mockStore) TLSConfig() *tls.Config { func (s *HelperTestSuite) SetUpSuite(c *C) { url := s.mockPDHTTPServer(c) time.Sleep(100 * time.Millisecond) - mvccStore := mocktikv.MustNewMVCCStore() - mockTikvStore, err := mockstore.NewMockTikvStore(mockstore.WithMVCCStore(mvccStore)) + mockTikvStore, err := mockstore.NewMockStore() s.store = &mockStore{ mockTikvStore.(tikv.Storage), []string{url[len("http://"):]}, diff --git a/store/mockstore/cluster/cluster.go b/store/mockstore/cluster/cluster.go index ee7f2fd7ac83e..569bb3717095a 100644 --- a/store/mockstore/cluster/cluster.go +++ b/store/mockstore/cluster/cluster.go @@ -32,7 +32,7 @@ type Cluster interface { GetAllStores() []*metapb.Store // ScheduleDelay schedules a delay event for a transaction on a region. ScheduleDelay(startTS, regionID uint64, dur time.Duration) - // SetMvccStore sets the the mvccStore used by SplitTable, SplitIndex and SplitKeys. + // Split splits a Region at the key (encoded) and creates new Region. Split(regionID, newRegionID uint64, key []byte, peerIDs []uint64, leaderPeerID uint64) // SplitRaw splits a Region at the key (not encoded) and creates new Region. SplitRaw(regionID, newRegionID uint64, rawKey []byte, peerIDs []uint64, leaderPeerID uint64) *metapb.Region diff --git a/store/mockstore/mockstore.go b/store/mockstore/mockstore.go new file mode 100644 index 0000000000000..5179edb45d6b7 --- /dev/null +++ b/store/mockstore/mockstore.go @@ -0,0 +1,200 @@ +// Copyright 2020 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package mockstore + +import ( + "net/url" + "strings" + + "github.com/pingcap/errors" + pd "github.com/pingcap/pd/v4/client" + "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/store/mockstore/cluster" + "github.com/pingcap/tidb/store/mockstore/mocktikv" + "github.com/pingcap/tidb/store/mockstore/unistore" + "github.com/pingcap/tidb/store/tikv" +) + +// MockTiKVDriver is in memory mock TiKV driver. +type MockTiKVDriver struct{} + +// Open creates a MockTiKV storage. +func (d MockTiKVDriver) Open(path string) (kv.Storage, error) { + u, err := url.Parse(path) + if err != nil { + return nil, errors.Trace(err) + } + if !strings.EqualFold(u.Scheme, "mocktikv") { + return nil, errors.Errorf("Uri scheme expected(mocktikv) but found (%s)", u.Scheme) + } + + opts := []MockTiKVStoreOption{WithPath(u.Path), WithStoreType(MockTiKV)} + txnLocalLatches := config.GetGlobalConfig().TxnLocalLatches + if txnLocalLatches.Enabled { + opts = append(opts, WithTxnLocalLatches(txnLocalLatches.Capacity)) + } + + return NewMockStore(opts...) +} + +// EmbedUnistoreDriver is in embedded unistore driver. +type EmbedUnistoreDriver struct{} + +// Open creates a EmbedUnistore storage. +func (d EmbedUnistoreDriver) Open(path string) (kv.Storage, error) { + u, err := url.Parse(path) + if err != nil { + return nil, errors.Trace(err) + } + if !strings.EqualFold(u.Scheme, "unistore") { + return nil, errors.Errorf("Uri scheme expected(unistore) but found (%s)", u.Scheme) + } + + opts := []MockTiKVStoreOption{WithPath(u.Path), WithStoreType(EmbedUnistore)} + txnLocalLatches := config.GetGlobalConfig().TxnLocalLatches + if txnLocalLatches.Enabled { + opts = append(opts, WithTxnLocalLatches(txnLocalLatches.Capacity)) + } + + return NewMockStore(opts...) +} + +// StoreType is the type of backend mock storage. +type StoreType uint8 + +const ( + // MockTiKV is the mock storage based on goleveldb. + MockTiKV StoreType = iota + // EmbedUnistore is the mock storage based on unistore. + EmbedUnistore + + defaultStoreType = MockTiKV +) + +type mockOptions struct { + clusterInspector func(cluster.Cluster) + clientHijacker func(tikv.Client) tikv.Client + pdClientHijacker func(pd.Client) pd.Client + path string + txnLocalLatches uint + storeType StoreType +} + +// MockTiKVStoreOption is used to control some behavior of mock tikv. +type MockTiKVStoreOption func(*mockOptions) + +// WithClientHijacker hijacks KV client's behavior, makes it easy to simulate the network +// problem between TiDB and TiKV. +func WithClientHijacker(hijacker func(tikv.Client) tikv.Client) MockTiKVStoreOption { + return func(c *mockOptions) { + c.clientHijacker = hijacker + } +} + +// WithPDClientHijacker hijacks PD client's behavior, makes it easy to simulate the network +// problem between TiDB and PD. +func WithPDClientHijacker(hijacker func(pd.Client) pd.Client) MockTiKVStoreOption { + return func(c *mockOptions) { + c.pdClientHijacker = hijacker + } +} + +// WithClusterInspector lets user to inspect the mock cluster handler. +func WithClusterInspector(inspector func(cluster.Cluster)) MockTiKVStoreOption { + return func(c *mockOptions) { + c.clusterInspector = inspector + } +} + +// WithStoreType lets user choose the backend storage's type. +func WithStoreType(tp StoreType) MockTiKVStoreOption { + return func(c *mockOptions) { + c.storeType = tp + } +} + +// WithPath specifies the mocktikv path. +func WithPath(path string) MockTiKVStoreOption { + return func(c *mockOptions) { + c.path = path + } +} + +// WithTxnLocalLatches enable txnLocalLatches, when capacity > 0. +func WithTxnLocalLatches(capacity uint) MockTiKVStoreOption { + return func(c *mockOptions) { + c.txnLocalLatches = capacity + } +} + +// NewMockStore creates a mocked tikv store, the path is the file path to store the data. +// If path is an empty string, a memory storage will be created. +func NewMockStore(options ...MockTiKVStoreOption) (kv.Storage, error) { + opt := mockOptions{ + clusterInspector: func(c cluster.Cluster) { + BootstrapWithSingleStore(c) + }, + storeType: defaultStoreType, + } + for _, f := range options { + f(&opt) + } + + switch opt.storeType { + case MockTiKV: + return newMockTikvStore(&opt) + case EmbedUnistore: + return newUnistore(&opt) + default: + panic("unsupported mockstore") + } +} + +// BootstrapWithSingleStore initializes a Cluster with 1 Region and 1 Store. +func BootstrapWithSingleStore(cluster cluster.Cluster) (storeID, peerID, regionID uint64) { + switch x := cluster.(type) { + case *mocktikv.Cluster: + return mocktikv.BootstrapWithSingleStore(x) + case *unistore.Cluster: + return unistore.BootstrapWithSingleStore(x) + default: + panic("unsupported cluster type") + } +} + +// BootstrapWithMultiStores initializes a Cluster with 1 Region and n Stores. +func BootstrapWithMultiStores(cluster cluster.Cluster, n int) (storeIDs, peerIDs []uint64, regionID uint64, leaderPeer uint64) { + switch x := cluster.(type) { + case *mocktikv.Cluster: + return mocktikv.BootstrapWithMultiStores(x, n) + case *unistore.Cluster: + return unistore.BootstrapWithMultiStores(x, n) + default: + panic("unsupported cluster type") + } +} + +// BootstrapWithMultiRegions initializes a Cluster with multiple Regions and 1 +// Store. The number of Regions will be len(splitKeys) + 1. +func BootstrapWithMultiRegions(cluster cluster.Cluster, splitKeys ...[]byte) (storeID uint64, regionIDs, peerIDs []uint64) { + switch x := cluster.(type) { + case *mocktikv.Cluster: + return mocktikv.BootstrapWithMultiRegions(x, splitKeys...) + case *unistore.Cluster: + return unistore.BootstrapWithMultiRegions(x, splitKeys...) + default: + panic("unsupported cluster type") + } +} diff --git a/store/mockstore/mocktikv/cluster.go b/store/mockstore/mocktikv/cluster.go index 4664941eee1ca..d2d9a4227c574 100644 --- a/store/mockstore/mocktikv/cluster.go +++ b/store/mockstore/mocktikv/cluster.go @@ -59,19 +59,15 @@ type delayKey struct { // NewCluster creates an empty cluster. It needs to be bootstrapped before // providing service. -func NewCluster() *Cluster { +func NewCluster(mvccStore MVCCStore) *Cluster { return &Cluster{ stores: make(map[uint64]*Store), regions: make(map[uint64]*Region), delayEvents: make(map[delayKey]time.Duration), + mvccStore: mvccStore, } } -// SetMvccStore sets the the mvccStore used by SplitTable, SplitIndex and SplitKeys. -func (c *Cluster) SetMvccStore(s MVCCStore) { - c.mvccStore = s -} - // AllocID creates an unique ID in cluster. The ID could be used as either // StoreID, RegionID, or PeerID. func (c *Cluster) AllocID() uint64 { diff --git a/store/mockstore/mocktikv/cluster_test.go b/store/mockstore/mocktikv/cluster_test.go index f1a8489662564..a281766ce6c92 100644 --- a/store/mockstore/mocktikv/cluster_test.go +++ b/store/mockstore/mocktikv/cluster_test.go @@ -24,8 +24,8 @@ import ( "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/mocktikv" + "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/tablecodec" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/codec" @@ -39,15 +39,13 @@ type testClusterSuite struct { } func (s *testClusterSuite) TestClusterSplit(c *C) { - cluster := mocktikv.NewCluster() + rpcClient, cluster, pdClient, err := mocktikv.NewTiKVAndPDClient("") + c.Assert(err, IsNil) mocktikv.BootstrapWithSingleStore(cluster) - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), - ) + mvccStore := rpcClient.MvccStore + store, err := tikv.NewTestTiKVStore(rpcClient, pdClient, nil, nil, 0) c.Assert(err, IsNil) + s.store = store txn, err := store.Begin() c.Assert(err, IsNil) diff --git a/store/mockstore/mocktikv/executor_test.go b/store/mockstore/mocktikv/executor_test.go index 1b81ba5c150a3..a9c964a4591ba 100644 --- a/store/mockstore/mocktikv/executor_test.go +++ b/store/mockstore/mocktikv/executor_test.go @@ -23,8 +23,8 @@ import ( "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/mocktikv" + "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/tablecodec" "github.com/pingcap/tidb/util/testkit" ) @@ -39,13 +39,12 @@ type testExecutorSuite struct { } func (s *testExecutorSuite) SetUpSuite(c *C) { - s.cluster = mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(s.cluster) - s.mvccStore = mocktikv.MustNewMVCCStore() - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(s.cluster), - mockstore.WithMVCCStore(s.mvccStore), - ) + rpcClient, cluster, pdClient, err := mocktikv.NewTiKVAndPDClient("") + c.Assert(err, IsNil) + mocktikv.BootstrapWithSingleStore(cluster) + s.cluster = cluster + s.mvccStore = rpcClient.MvccStore + store, err := tikv.NewTestTiKVStore(rpcClient, pdClient, nil, nil, 0) c.Assert(err, IsNil) s.store = store session.SetSchemaLease(0) diff --git a/store/mockstore/mocktikv/mock.go b/store/mockstore/mocktikv/mock.go index fe0ff2ef2df3e..e97b532bf507d 100644 --- a/store/mockstore/mocktikv/mock.go +++ b/store/mockstore/mocktikv/mock.go @@ -15,23 +15,16 @@ package mocktikv import ( "github.com/pingcap/errors" - "github.com/pingcap/pd/v4/client" + pd "github.com/pingcap/pd/v4/client" ) // NewTiKVAndPDClient creates a TiKV client and PD client from options. -func NewTiKVAndPDClient(cluster *Cluster, mvccStore MVCCStore, path string) (*RPCClient, pd.Client, error) { - if cluster == nil { - cluster = NewCluster() - BootstrapWithSingleStore(cluster) +func NewTiKVAndPDClient(path string) (*RPCClient, *Cluster, pd.Client, error) { + mvccStore, err := NewMVCCLevelDB(path) + if err != nil { + return nil, nil, nil, errors.Trace(err) } + cluster := NewCluster(mvccStore) - if mvccStore == nil { - var err error - mvccStore, err = NewMVCCLevelDB(path) - if err != nil { - return nil, nil, errors.Trace(err) - } - } - - return NewRPCClient(cluster, mvccStore), NewPDClient(cluster), nil + return NewRPCClient(cluster, mvccStore), cluster, NewPDClient(cluster), nil } diff --git a/store/mockstore/tikv.go b/store/mockstore/tikv.go index c9bd9c776522f..37aad76bb81d3 100644 --- a/store/mockstore/tikv.go +++ b/store/mockstore/tikv.go @@ -1,4 +1,4 @@ -// Copyright 2018 PingCAP, Inc. +// Copyright 2020 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,99 +14,20 @@ package mockstore import ( - "net/url" - "strings" - "github.com/pingcap/errors" - pd "github.com/pingcap/pd/v4/client" - "github.com/pingcap/tidb/config" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/store/tikv" ) -// MockDriver is in memory mock TiKV driver. -type MockDriver struct { -} - -// Open creates a MockTiKV storage. -func (d MockDriver) Open(path string) (kv.Storage, error) { - u, err := url.Parse(path) - if err != nil { - return nil, errors.Trace(err) - } - if !strings.EqualFold(u.Scheme, "mocktikv") { - return nil, errors.Errorf("Uri scheme expected(mocktikv) but found (%s)", u.Scheme) - } - - opts := []MockTiKVStoreOption{WithPath(u.Path)} - txnLocalLatches := config.GetGlobalConfig().TxnLocalLatches - if txnLocalLatches.Enabled { - opts = append(opts, WithTxnLocalLatches(txnLocalLatches.Capacity)) - } - return NewMockTikvStore(opts...) -} - -type mockOptions struct { - cluster *mocktikv.Cluster - mvccStore mocktikv.MVCCStore - clientHijack func(tikv.Client) tikv.Client - pdClientHijack func(pd.Client) pd.Client - path string - txnLocalLatches uint -} - -// MockTiKVStoreOption is used to control some behavior of mock tikv. -type MockTiKVStoreOption func(*mockOptions) - -// WithHijackClient hijacks KV client's behavior, makes it easy to simulate the network -// problem between TiDB and TiKV. -func WithHijackClient(wrap func(tikv.Client) tikv.Client) MockTiKVStoreOption { - return func(c *mockOptions) { - c.clientHijack = wrap - } -} - -// WithCluster provides the customized cluster. -func WithCluster(cluster *mocktikv.Cluster) MockTiKVStoreOption { - return func(c *mockOptions) { - c.cluster = cluster - } -} - -// WithMVCCStore provides the customized mvcc store. -func WithMVCCStore(store mocktikv.MVCCStore) MockTiKVStoreOption { - return func(c *mockOptions) { - c.mvccStore = store - } -} - -// WithPath specifies the mocktikv path. -func WithPath(path string) MockTiKVStoreOption { - return func(c *mockOptions) { - c.path = path - } -} - -// WithTxnLocalLatches enable txnLocalLatches, when capacity > 0. -func WithTxnLocalLatches(capacity uint) MockTiKVStoreOption { - return func(c *mockOptions) { - c.txnLocalLatches = capacity - } -} - -// NewMockTikvStore creates a mocked tikv store, the path is the file path to store the data. +// newMockTikvStore creates a mocked tikv store, the path is the file path to store the data. // If path is an empty string, a memory storage will be created. -func NewMockTikvStore(options ...MockTiKVStoreOption) (kv.Storage, error) { - var opt mockOptions - for _, f := range options { - f(&opt) - } - - client, pdClient, err := mocktikv.NewTiKVAndPDClient(opt.cluster, opt.mvccStore, opt.path) +func newMockTikvStore(opt *mockOptions) (kv.Storage, error) { + client, cluster, pdClient, err := mocktikv.NewTiKVAndPDClient(opt.path) if err != nil { return nil, errors.Trace(err) } + opt.clusterInspector(cluster) - return tikv.NewTestTiKVStore(client, pdClient, opt.clientHijack, opt.pdClientHijack, opt.txnLocalLatches) + return tikv.NewTestTiKVStore(client, pdClient, opt.clientHijacker, opt.pdClientHijacker, opt.txnLocalLatches) } diff --git a/store/mockstore/tikv_test.go b/store/mockstore/tikv_test.go index c638e6648b382..a1240d9599105 100644 --- a/store/mockstore/tikv_test.go +++ b/store/mockstore/tikv_test.go @@ -40,7 +40,7 @@ func (s testSuite) TestConfig(c *C) { IsLatchEnabled() bool } - var driver MockDriver + var driver MockTiKVDriver store, err := driver.Open("mocktikv://") c.Assert(err, IsNil) c.Assert(store.(LatchEnableChecker).IsLatchEnabled(), IsTrue) diff --git a/store/mockstore/unistore.go b/store/mockstore/unistore.go new file mode 100644 index 0000000000000..0e9e2cd44ff4d --- /dev/null +++ b/store/mockstore/unistore.go @@ -0,0 +1,31 @@ +// Copyright 2020 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package mockstore + +import ( + "github.com/pingcap/errors" + "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/store/mockstore/unistore" + "github.com/pingcap/tidb/store/tikv" +) + +func newUnistore(opts *mockOptions) (kv.Storage, error) { + client, pdClient, cluster, err := unistore.New(opts.path) + if err != nil { + return nil, errors.Trace(err) + } + opts.clusterInspector(cluster) + + return tikv.NewTestTiKVStore(client, pdClient, opts.clientHijacker, opts.pdClientHijacker, opts.txnLocalLatches) +} diff --git a/store/mockstore/unistore/cluster.go b/store/mockstore/unistore/cluster.go new file mode 100644 index 0000000000000..61715411f7d37 --- /dev/null +++ b/store/mockstore/unistore/cluster.go @@ -0,0 +1,138 @@ +// Copyright 2020 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package unistore + +import ( + "fmt" + "sync" + "time" + + us "github.com/ngaut/unistore/tikv" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/store/mockstore/cluster" +) + +type delayKey struct { + startTS uint64 + regionID uint64 +} + +var _ cluster.Cluster = new(Cluster) + +// Cluster simulates a TiKV cluster. It focuses on management and the change of +// meta data. A Cluster mainly includes following 3 kinds of meta data: +// 1) Region: A Region is a fragment of TiKV's data whose range is [start, end). +// The data of a Region is duplicated to multiple Peers and distributed in +// multiple Stores. +// 2) Peer: A Peer is a replica of a Region's data. All peers of a Region form +// a group, each group elects a Leader to provide services. +// 3) Store: A Store is a storage/service node. Try to think it as a TiKV server +// process. Only the store with request's Region's leader Peer could respond +// to client's request. +type Cluster struct { + *us.MockRegionManager + + // delayEvents is used to control the execution sequence of rpc requests for test. + delayEvents map[delayKey]time.Duration + delayMu sync.Mutex +} + +func newCluster(rm *us.MockRegionManager) *Cluster { + return &Cluster{ + MockRegionManager: rm, + delayEvents: make(map[delayKey]time.Duration), + } +} + +// ScheduleDelay schedules a delay event for a transaction on a region. +func (c *Cluster) ScheduleDelay(startTS, regionID uint64, dur time.Duration) { + c.delayMu.Lock() + c.delayEvents[delayKey{startTS: startTS, regionID: regionID}] = dur + c.delayMu.Unlock() +} + +func (c *Cluster) handleDelay(startTS, regionID uint64) { + key := delayKey{startTS: startTS, regionID: regionID} + c.delayMu.Lock() + dur, ok := c.delayEvents[key] + if ok { + delete(c.delayEvents, key) + } + c.delayMu.Unlock() + if ok { + time.Sleep(dur) + } +} + +// BootstrapWithSingleStore initializes a Cluster with 1 Region and 1 Store. +func BootstrapWithSingleStore(cluster *Cluster) (storeID, peerID, regionID uint64) { + storeID, regionID, peerID = cluster.AllocID(), cluster.AllocID(), cluster.AllocID() + store := &metapb.Store{ + Id: storeID, + Address: fmt.Sprintf("store%d", storeID), + } + region := &metapb.Region{ + Id: regionID, + RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []*metapb.Peer{{Id: peerID, StoreId: storeID}}, + } + if err := cluster.Bootstrap([]*metapb.Store{store}, region); err != nil { + panic(err) + } + return +} + +// BootstrapWithMultiStores initializes a Cluster with 1 Region and n Stores. +func BootstrapWithMultiStores(cluster *Cluster, n int) (storeIDs, peerIDs []uint64, regionID uint64, leaderPeer uint64) { + storeIDs = cluster.AllocIDs(n) + peerIDs = cluster.AllocIDs(n) + leaderPeer = peerIDs[0] + regionID = cluster.AllocID() + stores := make([]*metapb.Store, n) + for i, storeID := range storeIDs { + stores[i] = &metapb.Store{ + Id: storeID, + Address: fmt.Sprintf("store%d", storeID), + } + } + peers := make([]*metapb.Peer, n) + for i, peerID := range peerIDs { + peers[i] = &metapb.Peer{ + Id: peerID, + StoreId: storeIDs[i], + } + } + region := &metapb.Region{ + Id: regionID, + RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: peers, + } + if err := cluster.Bootstrap(stores, region); err != nil { + panic(err) + } + return +} + +// BootstrapWithMultiRegions initializes a Cluster with multiple Regions and 1 +// Store. The number of Regions will be len(splitKeys) + 1. +func BootstrapWithMultiRegions(cluster *Cluster, splitKeys ...[]byte) (storeID uint64, regionIDs, peerIDs []uint64) { + var firstRegionID, firstPeerID uint64 + storeID, firstPeerID, firstRegionID = BootstrapWithSingleStore(cluster) + regionIDs = append([]uint64{firstRegionID}, cluster.AllocIDs(len(splitKeys))...) + peerIDs = append([]uint64{firstPeerID}, cluster.AllocIDs(len(splitKeys))...) + for i, k := range splitKeys { + cluster.Split(regionIDs[i], regionIDs[i+1], k, []uint64{peerIDs[i]}, peerIDs[i]) + } + return +} diff --git a/store/mockstore/unistore/mock.go b/store/mockstore/unistore/mock.go new file mode 100644 index 0000000000000..647354e1fc17d --- /dev/null +++ b/store/mockstore/unistore/mock.go @@ -0,0 +1,68 @@ +// Copyright 2020 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package unistore + +import ( + "io/ioutil" + "os" + + usconf "github.com/ngaut/unistore/config" + ussvr "github.com/ngaut/unistore/server" + "github.com/pingcap/errors" + pd "github.com/pingcap/pd/v4/client" +) + +// New creates a embed unistore client, pd client and cluster handler. +func New(path string) (*RPCClient, pd.Client, *Cluster, error) { + persistent := true + if path == "" { + var err error + if path, err = ioutil.TempDir("", "tidb-unistore-temp"); err != nil { + return nil, nil, nil, err + } + persistent = false + } + + if err := os.MkdirAll(path, 0777); err != nil { + return nil, nil, nil, err + } + + conf := usconf.DefaultConf + conf.Engine.DBPath = path + conf.Server.Raft = false + + if !persistent { + conf.Engine.VolatileMode = true + conf.Engine.MaxMemTableSize = 12 << 20 + conf.Engine.SyncWrite = false + conf.Engine.NumCompactors = 1 + conf.Engine.CompactL0WhenClose = false + } + + srv, rm, pd, err := ussvr.NewMock(&conf, 1) + if err != nil { + return nil, nil, nil, errors.Trace(err) + } + + cluster := newCluster(rm) + client := &RPCClient{ + usSvr: srv, + cluster: cluster, + path: path, + persistent: persistent, + } + pdClient := newPDClient(pd) + + return client, pdClient, cluster, nil +} diff --git a/store/mockstore/unistore/pd.go b/store/mockstore/unistore/pd.go new file mode 100644 index 0000000000000..4e197bd302aaa --- /dev/null +++ b/store/mockstore/unistore/pd.go @@ -0,0 +1,98 @@ +// Copyright 2020 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package unistore + +import ( + "errors" + "math" + "sync" + + us "github.com/ngaut/unistore/tikv" + "github.com/pingcap/kvproto/pkg/pdpb" + pd "github.com/pingcap/pd/v4/client" + "golang.org/x/net/context" +) + +var _ pd.Client = new(pdClient) + +type pdClient struct { + *us.MockPD + + serviceSafePoints map[string]uint64 + gcSafePointMu sync.Mutex +} + +func newPDClient(pd *us.MockPD) *pdClient { + return &pdClient{ + MockPD: pd, + serviceSafePoints: make(map[string]uint64), + } +} + +func (c *pdClient) GetTSAsync(ctx context.Context) pd.TSFuture { + return &mockTSFuture{c, ctx, false} +} + +type mockTSFuture struct { + pdc *pdClient + ctx context.Context + used bool +} + +func (m *mockTSFuture) Wait() (int64, int64, error) { + if m.used { + return 0, 0, errors.New("cannot wait tso twice") + } + m.used = true + return m.pdc.GetTS(m.ctx) +} + +func (c *pdClient) ConfigClient() pd.ConfigClient { + return nil +} + +func (c *pdClient) GetLeaderAddr() string { return "mockpd" } + +func (c *pdClient) UpdateServiceGCSafePoint(ctx context.Context, serviceID string, ttl int64, safePoint uint64) (uint64, error) { + c.gcSafePointMu.Lock() + defer c.gcSafePointMu.Unlock() + + if ttl == 0 { + delete(c.serviceSafePoints, serviceID) + } else { + var minSafePoint uint64 = math.MaxUint64 + for _, ssp := range c.serviceSafePoints { + if ssp < minSafePoint { + minSafePoint = ssp + } + } + + if len(c.serviceSafePoints) == 0 || minSafePoint <= safePoint { + c.serviceSafePoints[serviceID] = safePoint + } + } + + // The minSafePoint may have changed. Reload it. + var minSafePoint uint64 = math.MaxUint64 + for _, ssp := range c.serviceSafePoints { + if ssp < minSafePoint { + minSafePoint = ssp + } + } + return minSafePoint, nil +} + +func (c *pdClient) GetOperator(ctx context.Context, regionID uint64) (*pdpb.GetOperatorResponse, error) { + return &pdpb.GetOperatorResponse{Status: pdpb.OperatorStatus_SUCCESS}, nil +} diff --git a/store/mockstore/unistore/rpc.go b/store/mockstore/unistore/rpc.go new file mode 100644 index 0000000000000..24be44949c3e8 --- /dev/null +++ b/store/mockstore/unistore/rpc.go @@ -0,0 +1,309 @@ +// Copyright 2020 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package unistore + +import ( + "io" + "math" + "os" + "strconv" + "sync" + "time" + + "github.com/golang/protobuf/proto" + us "github.com/ngaut/unistore/tikv" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/kvproto/pkg/debugpb" + "github.com/pingcap/kvproto/pkg/errorpb" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/parser/terror" + "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/store/tikv/tikvrpc" + "github.com/pingcap/tidb/util/codec" + "golang.org/x/net/context" + "google.golang.org/grpc/metadata" +) + +// For gofail injection. +var undeterminedErr = terror.ErrResultUndetermined + +// RPCClient sends kv RPC calls to mock cluster. RPCClient mocks the behavior of +// a rpc client at tikv's side. +type RPCClient struct { + usSvr *us.Server + cluster *Cluster + path string + persistent bool + + // rpcCli uses to redirects RPC request to TiDB rpc server, It is only use for test. + // Mock TiDB rpc service will have circle import problem, so just use a real RPC client to send this RPC server. + // sync.Once uses to avoid concurrency initialize rpcCli. + sync.Once + rpcCli Client +} + +// SendRequest sends a request to mock cluster. +func (c *RPCClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { + failpoint.Inject("rpcServerBusy", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(tikvrpc.GenRegionErrorResp(req, &errorpb.Error{ServerIsBusy: &errorpb.ServerIsBusy{}})) + } + }) + + if req.StoreTp == kv.TiDB { + return c.redirectRequestToRPCServer(ctx, addr, req, timeout) + } + + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + resp := &tikvrpc.Response{} + var err error + switch req.Type { + case tikvrpc.CmdGet: + resp.Resp, err = c.usSvr.KvGet(ctx, req.Get()) + case tikvrpc.CmdScan: + resp.Resp, err = c.usSvr.KvScan(ctx, req.Scan()) + case tikvrpc.CmdPrewrite: + failpoint.Inject("rpcPrewriteResult", func(val failpoint.Value) { + switch val.(string) { + case "notLeader": + failpoint.Return(&tikvrpc.Response{ + Resp: &kvrpcpb.PrewriteResponse{RegionError: &errorpb.Error{NotLeader: &errorpb.NotLeader{}}}, + }, nil) + } + }) + + r := req.Prewrite() + c.cluster.handleDelay(r.StartVersion, r.Context.RegionId) + resp.Resp, err = c.usSvr.KvPrewrite(ctx, r) + case tikvrpc.CmdPessimisticLock: + r := req.PessimisticLock() + c.cluster.handleDelay(r.StartVersion, r.Context.RegionId) + resp.Resp, err = c.usSvr.KvPessimisticLock(ctx, r) + case tikvrpc.CmdPessimisticRollback: + resp.Resp, err = c.usSvr.KVPessimisticRollback(ctx, req.PessimisticRollback()) + case tikvrpc.CmdCommit: + failpoint.Inject("rpcCommitResult", func(val failpoint.Value) { + switch val.(string) { + case "timeout": + failpoint.Return(nil, errors.New("timeout")) + case "notLeader": + failpoint.Return(&tikvrpc.Response{ + Resp: &kvrpcpb.CommitResponse{RegionError: &errorpb.Error{NotLeader: &errorpb.NotLeader{}}}, + }, nil) + case "keyError": + failpoint.Return(&tikvrpc.Response{ + Resp: &kvrpcpb.CommitResponse{Error: &kvrpcpb.KeyError{}}, + }, nil) + } + }) + + resp.Resp, err = c.usSvr.KvCommit(ctx, req.Commit()) + + failpoint.Inject("rpcCommitTimeout", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(nil, undeterminedErr) + } + }) + case tikvrpc.CmdCleanup: + resp.Resp, err = c.usSvr.KvCleanup(ctx, req.Cleanup()) + case tikvrpc.CmdCheckTxnStatus: + resp.Resp, err = c.usSvr.KvCheckTxnStatus(ctx, req.CheckTxnStatus()) + case tikvrpc.CmdTxnHeartBeat: + resp.Resp, err = c.usSvr.KvTxnHeartBeat(ctx, req.TxnHeartBeat()) + case tikvrpc.CmdBatchGet: + resp.Resp, err = c.usSvr.KvBatchGet(ctx, req.BatchGet()) + case tikvrpc.CmdBatchRollback: + resp.Resp, err = c.usSvr.KvBatchRollback(ctx, req.BatchRollback()) + case tikvrpc.CmdScanLock: + resp.Resp, err = c.usSvr.KvScanLock(ctx, req.ScanLock()) + case tikvrpc.CmdResolveLock: + resp.Resp, err = c.usSvr.KvResolveLock(ctx, req.ResolveLock()) + case tikvrpc.CmdGC: + resp.Resp, err = c.usSvr.KvGC(ctx, req.GC()) + case tikvrpc.CmdDeleteRange: + resp.Resp, err = c.usSvr.KvDeleteRange(ctx, req.DeleteRange()) + case tikvrpc.CmdRawGet: + resp.Resp, err = c.usSvr.RawGet(ctx, req.RawGet()) + case tikvrpc.CmdRawBatchGet: + resp.Resp, err = c.usSvr.RawBatchGet(ctx, req.RawBatchGet()) + case tikvrpc.CmdRawPut: + resp.Resp, err = c.usSvr.RawPut(ctx, req.RawPut()) + case tikvrpc.CmdRawBatchPut: + resp.Resp, err = c.usSvr.RawBatchPut(ctx, req.RawBatchPut()) + case tikvrpc.CmdRawDelete: + resp.Resp, err = c.usSvr.RawDelete(ctx, req.RawDelete()) + case tikvrpc.CmdRawBatchDelete: + resp.Resp, err = c.usSvr.RawBatchDelete(ctx, req.RawBatchDelete()) + case tikvrpc.CmdRawDeleteRange: + resp.Resp, err = c.usSvr.RawDeleteRange(ctx, req.RawDeleteRange()) + case tikvrpc.CmdRawScan: + resp.Resp, err = c.usSvr.RawScan(ctx, req.RawScan()) + case tikvrpc.CmdCop: + resp.Resp, err = c.usSvr.Coprocessor(ctx, req.Cop()) + case tikvrpc.CmdCopStream: + resp.Resp, err = c.handleCopStream(ctx, req.Cop()) + case tikvrpc.CmdMvccGetByKey: + resp.Resp, err = c.usSvr.MvccGetByKey(ctx, req.MvccGetByKey()) + case tikvrpc.CmdMvccGetByStartTs: + resp.Resp, err = c.usSvr.MvccGetByStartTs(ctx, req.MvccGetByStartTs()) + case tikvrpc.CmdSplitRegion: + resp.Resp, err = c.usSvr.SplitRegion(ctx, req.SplitRegion()) + case tikvrpc.CmdDebugGetRegionProperties: + resp.Resp, err = c.handleDebugGetRegionProperties(ctx, req.DebugGetRegionProperties()) + return resp, err + default: + err = errors.Errorf("unsupport this request type %v", req.Type) + } + if err != nil { + return nil, err + } + regErr, err := resp.GetRegionError() + if err != nil { + return nil, err + } + if regErr != nil { + if regErr.EpochNotMatch != nil { + for i, newReg := range regErr.EpochNotMatch.CurrentRegions { + regErr.EpochNotMatch.CurrentRegions[i] = proto.Clone(newReg).(*metapb.Region) + } + } + } + return resp, nil +} + +func (c *RPCClient) handleCopStream(ctx context.Context, req *coprocessor.Request) (*tikvrpc.CopStreamResponse, error) { + copResp, err := c.usSvr.Coprocessor(ctx, req) + if err != nil { + return nil, err + } + return &tikvrpc.CopStreamResponse{ + Tikv_CoprocessorStreamClient: new(mockCopStreamClient), + Response: copResp, + }, nil +} + +func (c *RPCClient) handleDebugGetRegionProperties(ctx context.Context, req *debugpb.GetRegionPropertiesRequest) (*debugpb.GetRegionPropertiesResponse, error) { + region := c.cluster.GetRegion(req.RegionId) + _, start, err := codec.DecodeBytes(region.StartKey, nil) + if err != nil { + return nil, err + } + _, end, err := codec.DecodeBytes(region.EndKey, nil) + if err != nil { + return nil, err + } + scanResp, err := c.usSvr.KvScan(ctx, &kvrpcpb.ScanRequest{ + Context: &kvrpcpb.Context{ + RegionId: region.Id, + RegionEpoch: region.RegionEpoch, + }, + StartKey: start, + EndKey: end, + Version: math.MaxUint64, + Limit: math.MaxUint32, + }) + if err != nil { + return nil, err + } + if err := scanResp.GetRegionError(); err != nil { + panic(err) + } + return &debugpb.GetRegionPropertiesResponse{ + Props: []*debugpb.Property{{ + Name: "mvcc.num_rows", + Value: strconv.Itoa(len(scanResp.Pairs)), + }}}, nil +} + +// Client is a client that sends RPC. +// This is same with tikv.Client, define again for avoid circle import. +type Client interface { + // Close should release all data. + Close() error + // SendRequest sends Request. + SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) +} + +// GRPCClientFactory is the GRPC client factory. +// Use global variable to avoid circle import. +// TODO: remove this global variable. +var GRPCClientFactory func() Client + +// redirectRequestToRPCServer redirects RPC request to TiDB rpc server, It is only use for test. +// Mock TiDB rpc service will have circle import problem, so just use a real RPC client to send this RPC server. +func (c *RPCClient) redirectRequestToRPCServer(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { + c.Once.Do(func() { + if GRPCClientFactory != nil { + c.rpcCli = GRPCClientFactory() + } + }) + if c.rpcCli == nil { + return nil, errors.Errorf("GRPCClientFactory is nil") + } + return c.rpcCli.SendRequest(ctx, addr, req, timeout) +} + +// Close closes RPCClient and cleanup temporal resources. +func (c *RPCClient) Close() error { + if c.usSvr != nil { + c.usSvr.Stop() + } + if c.rpcCli != nil { + err := c.rpcCli.Close() + if err != nil { + return err + } + } + if !c.persistent && c.path != "" { + err := os.RemoveAll(c.path) + _ = err + } + return nil +} + +type mockClientStream struct{} + +// Header implements grpc.ClientStream interface +func (mockClientStream) Header() (metadata.MD, error) { return nil, nil } + +// Trailer implements grpc.ClientStream interface +func (mockClientStream) Trailer() metadata.MD { return nil } + +// CloseSend implements grpc.ClientStream interface +func (mockClientStream) CloseSend() error { return nil } + +// Context implements grpc.ClientStream interface +func (mockClientStream) Context() context.Context { return nil } + +// SendMsg implements grpc.ClientStream interface +func (mockClientStream) SendMsg(m interface{}) error { return nil } + +// RecvMsg implements grpc.ClientStream interface +func (mockClientStream) RecvMsg(m interface{}) error { return nil } + +type mockCopStreamClient struct { + mockClientStream +} + +func (mock *mockCopStreamClient) Recv() (*coprocessor.Response, error) { + return nil, io.EOF +} diff --git a/store/store_test.go b/store/store_test.go index 13bad3ce2172f..066d7334e48d4 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -57,7 +57,7 @@ type testKVSuite struct { func (s *testKVSuite) SetUpSuite(c *C) { testleak.BeforeTest() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) s.s = store } @@ -530,7 +530,7 @@ func (s *testKVSuite) TestConditionUpdate(c *C) { func (s *testKVSuite) TestDBClose(c *C) { c.Skip("don't know why it fails.") - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) txn, err := store.Begin() diff --git a/store/tikv/2pc_test.go b/store/tikv/2pc_test.go index b045805dae33b..ab36004414a5d 100644 --- a/store/tikv/2pc_test.go +++ b/store/tikv/2pc_test.go @@ -49,18 +49,32 @@ func (s *testCommitterSuite) SetUpSuite(c *C) { } func (s *testCommitterSuite) SetUpTest(c *C) { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithMultiRegions(cluster, []byte("a"), []byte("b"), []byte("c")) - s.cluster = cluster mvccStore, err := mocktikv.NewMVCCLevelDB("") c.Assert(err, IsNil) - cluster.SetMvccStore(mvccStore) + cluster := mocktikv.NewCluster(mvccStore) + mocktikv.BootstrapWithMultiRegions(cluster, []byte("a"), []byte("b"), []byte("c")) + s.cluster = cluster client := mocktikv.NewRPCClient(cluster, mvccStore) pdCli := &codecPDClient{mocktikv.NewPDClient(cluster)} spkv := NewMockSafePointKV() store, err := newTikvStore("mocktikv-store", pdCli, spkv, client, false, nil) - c.Assert(err, IsNil) store.EnableTxnLocalLatches(1024000) + c.Assert(err, IsNil) + + // TODO: make it possible + // store, err := mockstore.NewMockStore( + // mockstore.WithStoreType(mockstore.MockTiKV), + // mockstore.WithClusterInspector(func(c cluster.Cluster) { + // mockstore.BootstrapWithMultiRegions(c, []byte("a"), []byte("b"), []byte("c")) + // s.cluster = c + // }), + // mockstore.WithPDClientHijacker(func(c pd.Client) pd.Client { + // return &codecPDClient{c} + // }), + // mockstore.WithTxnLocalLatches(1024000), + // ) + // c.Assert(err, IsNil) + s.store = store CommitMaxBackoff = 1000 } diff --git a/store/tikv/coprocessor_test.go b/store/tikv/coprocessor_test.go index 8f353a59d9e22..ce072ce0b470e 100644 --- a/store/tikv/coprocessor_test.go +++ b/store/tikv/coprocessor_test.go @@ -31,7 +31,7 @@ var _ = Suite(&testCoprocessorSuite{}) func (s *testCoprocessorSuite) TestBuildTasks(c *C) { // nil --- 'g' --- 'n' --- 't' --- nil // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - cluster := mocktikv.NewCluster() + cluster := mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) _, regionIDs, _ := mocktikv.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) pdCli := &codecPDClient{mocktikv.NewPDClient(cluster)} cache := NewRegionCache(pdCli) @@ -148,7 +148,7 @@ func (s *testCoprocessorSuite) TestBuildTasks(c *C) { func (s *testCoprocessorSuite) TestSplitRegionRanges(c *C) { // nil --- 'g' --- 'n' --- 't' --- nil // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - cluster := mocktikv.NewCluster() + cluster := mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) mocktikv.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) pdCli := &codecPDClient{mocktikv.NewPDClient(cluster)} cache := NewRegionCache(pdCli) @@ -201,7 +201,7 @@ func (s *testCoprocessorSuite) TestSplitRegionRanges(c *C) { func (s *testCoprocessorSuite) TestRebuild(c *C) { // nil --- 'm' --- nil // <- 0 -> <- 1 -> - cluster := mocktikv.NewCluster() + cluster := mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) storeID, regionIDs, peerIDs := mocktikv.BootstrapWithMultiRegions(cluster, []byte("m")) pdCli := &codecPDClient{mocktikv.NewPDClient(cluster)} cache := NewRegionCache(pdCli) diff --git a/store/tikv/delete_range_test.go b/store/tikv/delete_range_test.go index 508413c75910f..bf14d221617fe 100644 --- a/store/tikv/delete_range_test.go +++ b/store/tikv/delete_range_test.go @@ -33,14 +33,23 @@ type testDeleteRangeSuite struct { var _ = Suite(&testDeleteRangeSuite{}) func (s *testDeleteRangeSuite) SetUpTest(c *C) { - cluster := mocktikv.NewCluster() + client, cluster, pdClient, err := mocktikv.NewTiKVAndPDClient("") + c.Assert(err, IsNil) mocktikv.BootstrapWithMultiRegions(cluster, []byte("b"), []byte("c"), []byte("d")) s.cluster = cluster - client, pdClient, err := mocktikv.NewTiKVAndPDClient(cluster, nil, "") - c.Assert(err, IsNil) - store, err := NewTestTiKVStore(client, pdClient, nil, nil, 0) c.Check(err, IsNil) + + // TODO: make this possible + // store, err := mockstore.NewMockStore( + // mockstore.WithStoreType(mockstore.MockTiKV), + // mockstore.WithClusterInspector(func(c cluster.Cluster) { + // mockstore.BootstrapWithMultiRegions(c, []byte("b"), []byte("c"), []byte("d")) + // s.cluster = c + // }), + // ) + // c.Assert(err, IsNil) + s.store = store.(*tikvStore) } diff --git a/store/tikv/gcworker/gc_worker_test.go b/store/tikv/gcworker/gc_worker_test.go index 009fda92c6ae9..a48c5ca305d59 100644 --- a/store/tikv/gcworker/gc_worker_test.go +++ b/store/tikv/gcworker/gc_worker_test.go @@ -41,7 +41,6 @@ import ( "github.com/pingcap/tidb/store/mockoracle" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/store/tikv/oracle" "github.com/pingcap/tidb/store/tikv/tikvrpc" @@ -74,12 +73,18 @@ func (s *testGCWorkerSuite) SetUpTest(c *C) { return client } - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithMultiStores(cluster, 3) - s.cluster = cluster - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithHijackClient(hijackClient)) + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithMultiStores(c, 3) + s.cluster = c + }), + mockstore.WithClientHijacker(hijackClient), + mockstore.WithPDClientHijacker(func(c pd.Client) pd.Client { + s.pdClient = c + return c + }), + ) + c.Assert(err, IsNil) s.store = store.(tikv.Storage) c.Assert(err, IsNil) @@ -88,7 +93,6 @@ func (s *testGCWorkerSuite) SetUpTest(c *C) { s.dom, err = session.BootstrapSession(s.store) c.Assert(err, IsNil) - s.pdClient = mocktikv.NewPDClient(cluster) gcWorker, err := NewGCWorker(s.store, s.pdClient) c.Assert(err, IsNil) gcWorker.Start() @@ -127,7 +131,11 @@ func (s *testGCWorkerSuite) mustGetNone(c *C, key string, ts uint64) { snap, err := s.store.GetSnapshot(kv.Version{Ver: ts}) c.Assert(err, IsNil) _, err = snap.Get(context.TODO(), []byte(key)) - c.Assert(err, Equals, kv.ErrNotExist) + if err != nil { + // Unistore's gc is based on compaction filter. + // So skip the error check if err == nil. + c.Assert(err, Equals, kv.ErrNotExist) + } } func (s *testGCWorkerSuite) mustAllocTs(c *C) uint64 { diff --git a/store/tikv/range_task_test.go b/store/tikv/range_task_test.go index 06baee0f3ba91..e72e148fce1db 100644 --- a/store/tikv/range_task_test.go +++ b/store/tikv/range_task_test.go @@ -60,14 +60,23 @@ func (s *testRangeTaskSuite) SetUpTest(c *C) { } allRegionRanges = append(allRegionRanges, makeRange("z", "")) - cluster := mocktikv.NewCluster() + client, cluster, pdClient, err := mocktikv.NewTiKVAndPDClient("") + c.Assert(err, IsNil) mocktikv.BootstrapWithMultiRegions(cluster, splitKeys...) s.cluster = cluster - client, pdClient, err := mocktikv.NewTiKVAndPDClient(cluster, nil, "") - c.Assert(err, IsNil) store, err := NewTestTiKVStore(client, pdClient, nil, nil, 0) c.Assert(err, IsNil) + + // TODO: make this possible + // store, err := mockstore.NewMockStore( + // mockstore.WithStoreType(mockstore.MockTiKV), + // mockstore.WithClusterInspector(func(c cluster.Cluster) { + // mockstore.BootstrapWithMultiRegions(c, splitKeys...) + // s.cluster = c + // }), + // ) + // c.Assert(err, IsNil) s.store = store.(*tikvStore) s.testRanges = []kv.KeyRange{ diff --git a/store/tikv/rawkv_test.go b/store/tikv/rawkv_test.go index f5bd479946819..3a2197f3093bc 100644 --- a/store/tikv/rawkv_test.go +++ b/store/tikv/rawkv_test.go @@ -33,7 +33,7 @@ type testRawKVSuite struct { var _ = Suite(&testRawKVSuite{}) func (s *testRawKVSuite) SetUpTest(c *C) { - cluster := mocktikv.NewCluster() + cluster := mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) mocktikv.BootstrapWithSingleStore(cluster) s.cluster = cluster pdClient := mocktikv.NewPDClient(cluster) diff --git a/store/tikv/region_cache_test.go b/store/tikv/region_cache_test.go index 06cdca6d56cdb..86f6e25b694ac 100644 --- a/store/tikv/region_cache_test.go +++ b/store/tikv/region_cache_test.go @@ -45,7 +45,7 @@ type testRegionCacheSuite struct { var _ = Suite(&testRegionCacheSuite{}) func (s *testRegionCacheSuite) SetUpTest(c *C) { - s.cluster = mocktikv.NewCluster() + s.cluster = mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) storeIDs, peerIDs, regionID, _ := mocktikv.BootstrapWithMultiStores(s.cluster, 2) s.region1 = regionID s.store1 = storeIDs[0] @@ -678,7 +678,7 @@ func (s *testRegionCacheSuite) TestRegionEpochAheadOfTiKV(c *C) { const regionSplitKeyFormat = "t%08d" func createClusterWithStoresAndRegions(regionCnt, storeCount int) *mocktikv.Cluster { - cluster := mocktikv.NewCluster() + cluster := mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) _, _, regionID, _ := mocktikv.BootstrapWithMultiStores(cluster, storeCount) for i := 0; i < regionCnt; i++ { rawKey := []byte(fmt.Sprintf(regionSplitKeyFormat, i)) diff --git a/store/tikv/region_request_test.go b/store/tikv/region_request_test.go index 8c4dad22b0f4d..300ce59062592 100644 --- a/store/tikv/region_request_test.go +++ b/store/tikv/region_request_test.go @@ -61,7 +61,7 @@ var _ = Suite(&testRegionRequestSuite{}) var _ = Suite(&testStoreLimitSuite{}) func (s *testRegionRequestSuite) SetUpTest(c *C) { - s.cluster = mocktikv.NewCluster() + s.cluster = mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) s.store, s.peer, s.region = mocktikv.BootstrapWithSingleStore(s.cluster) pdCli := &codecPDClient{mocktikv.NewPDClient(s.cluster)} s.cache = NewRegionCache(pdCli) @@ -72,7 +72,7 @@ func (s *testRegionRequestSuite) SetUpTest(c *C) { } func (s *testStoreLimitSuite) SetUpTest(c *C) { - s.cluster = mocktikv.NewCluster() + s.cluster = mocktikv.NewCluster(mocktikv.MustNewMVCCStore()) s.storeIDs, s.peerIDs, s.regionID, s.leaderPeer = mocktikv.BootstrapWithMultiStores(s.cluster, 3) pdCli := &codecPDClient{mocktikv.NewPDClient(s.cluster)} s.cache = NewRegionCache(pdCli) diff --git a/store/tikv/split_test.go b/store/tikv/split_test.go index 3ed762909b81a..4a1b6ee234f20 100644 --- a/store/tikv/split_test.go +++ b/store/tikv/split_test.go @@ -32,14 +32,21 @@ type testSplitSuite struct { var _ = Suite(&testSplitSuite{}) func (s *testSplitSuite) SetUpTest(c *C) { - cluster := mocktikv.NewCluster() + client, cluster, pdClient, err := mocktikv.NewTiKVAndPDClient("") + c.Assert(err, IsNil) mocktikv.BootstrapWithSingleStore(cluster) s.cluster = cluster - client, pdClient, err := mocktikv.NewTiKVAndPDClient(cluster, nil, "") + store, err := NewTestTiKVStore(client, pdClient, nil, nil, 0) c.Assert(err, IsNil) - store, err := NewTestTiKVStore(client, pdClient, nil, nil, 0) - c.Check(err, IsNil) + // TODO: make this possible + // store, err := mockstore.NewMockStore( + // mockstore.WithClusterInspector(func(c cluster.Cluster) { + // mockstore.BootstrapWithSingleStore(c) + // s.cluster = c + // }), + // ) + // c.Assert(err, IsNil) s.store = store.(*tikvStore) s.bo = NewBackoffer(context.Background(), 5000) } diff --git a/store/tikv/ticlient_test.go b/store/tikv/ticlient_test.go index 9639a77e5ca12..2695891f74461 100644 --- a/store/tikv/ticlient_test.go +++ b/store/tikv/ticlient_test.go @@ -48,11 +48,17 @@ func NewTestStore(c *C) kv.Storage { return store } - client, pdClient, err := mocktikv.NewTiKVAndPDClient(nil, nil, "") + client, cluster, pdClient, err := mocktikv.NewTiKVAndPDClient("") c.Assert(err, IsNil) + mocktikv.BootstrapWithSingleStore(cluster) store, err := NewTestTiKVStore(client, pdClient, nil, nil, 0) c.Assert(err, IsNil) + + // TODO: make this possible + // store, err := mockstore.NewMockStore() + // c.Assert(err, IsNil) + return store } diff --git a/structure/structure_test.go b/structure/structure_test.go index 62fdeb8f60159..ece0871c6823b 100644 --- a/structure/structure_test.go +++ b/structure/structure_test.go @@ -39,7 +39,7 @@ type testTxStructureSuite struct { func (s *testTxStructureSuite) SetUpSuite(c *C) { testleak.BeforeTest() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) s.store = store } diff --git a/table/tables/index_test.go b/table/tables/index_test.go index bf5eea009cc62..b77277bf10ac4 100644 --- a/table/tables/index_test.go +++ b/table/tables/index_test.go @@ -50,7 +50,7 @@ type testIndexSuite struct { func (s *testIndexSuite) SetUpSuite(c *C) { testleak.BeforeTest() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) s.s = store s.dom, err = session.BootstrapSession(store) diff --git a/table/tables/tables_test.go b/table/tables/tables_test.go index 04f9144ca6ec2..e1ac7ff7543a8 100644 --- a/table/tables/tables_test.go +++ b/table/tables/tables_test.go @@ -54,7 +54,7 @@ type testSuite struct { func (ts *testSuite) SetUpSuite(c *C) { testleak.BeforeTest() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Check(err, IsNil) ts.store = store ts.dom, err = session.BootstrapSession(store) diff --git a/tidb-server/main.go b/tidb-server/main.go index 24161f39c3334..325a607857c9c 100644 --- a/tidb-server/main.go +++ b/tidb-server/main.go @@ -32,7 +32,7 @@ import ( "github.com/pingcap/log" "github.com/pingcap/parser/mysql" "github.com/pingcap/parser/terror" - "github.com/pingcap/pd/v4/client" + pd "github.com/pingcap/pd/v4/client" pumpcli "github.com/pingcap/tidb-tools/tidb-binlog/pump_client" "github.com/pingcap/tidb/bindinfo" "github.com/pingcap/tidb/config" @@ -286,7 +286,9 @@ func registerStores() { err := kvstore.Register("tikv", tikv.Driver{}) terror.MustNil(err) tikv.NewGCHandlerFunc = gcworker.NewGCWorker - err = kvstore.Register("mocktikv", mockstore.MockDriver{}) + err = kvstore.Register("mocktikv", mockstore.MockTiKVDriver{}) + terror.MustNil(err) + err = kvstore.Register("unistore", mockstore.EmbedUnistoreDriver{}) terror.MustNil(err) } diff --git a/types/const_test.go b/types/const_test.go index e5e46037e796a..626bd1f60bb48 100644 --- a/types/const_test.go +++ b/types/const_test.go @@ -24,6 +24,7 @@ import ( "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore" + "github.com/pingcap/tidb/store/mockstore/cluster" "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/util/testkit" "github.com/pingcap/tidb/util/testleak" @@ -32,7 +33,7 @@ import ( var _ = Suite(&testMySQLConstSuite{}) type testMySQLConstSuite struct { - cluster *mocktikv.Cluster + cluster cluster.Cluster mvccStore mocktikv.MVCCStore store kv.Storage dom *domain.Domain @@ -46,12 +47,11 @@ func (s *testMySQLConstSuite) SetUpSuite(c *C) { flag.Lookup("mockTikv") useMockTikv := *mockTikv if useMockTikv { - s.cluster = mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(s.cluster) - s.mvccStore = mocktikv.MustNewMVCCStore() - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(s.cluster), - mockstore.WithMVCCStore(s.mvccStore), + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.store = store diff --git a/util/admin/admin_integration_test.go b/util/admin/admin_integration_test.go index 003f6ce984d28..36da738e6076c 100644 --- a/util/admin/admin_integration_test.go +++ b/util/admin/admin_integration_test.go @@ -20,7 +20,6 @@ import ( "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/store/mockstore/cluster" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/util/testkit" ) @@ -33,15 +32,11 @@ type testAdminSuite struct { } func (s *testAdminSuite) SetUpSuite(c *C) { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - s.cluster = cluster - - mvccStore := mocktikv.MustNewMVCCStore() - cluster.SetMvccStore(mvccStore) - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c cluster.Cluster) { + mockstore.BootstrapWithSingleStore(c) + s.cluster = c + }), ) c.Assert(err, IsNil) s.store = store diff --git a/util/admin/admin_test.go b/util/admin/admin_test.go index 9a55a2d4f90db..d3f28d6014af9 100644 --- a/util/admin/admin_test.go +++ b/util/admin/admin_test.go @@ -43,7 +43,7 @@ type testSuite struct { func (s *testSuite) SetUpSuite(c *C) { testleak.BeforeTest() var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) s.ctx = mock.NewContext() s.ctx.Store = s.store diff --git a/util/prefix_helper_test.go b/util/prefix_helper_test.go index 2c0543406ac9e..114fee97d5449 100644 --- a/util/prefix_helper_test.go +++ b/util/prefix_helper_test.go @@ -45,7 +45,7 @@ type testPrefixSuite struct { func (s *testPrefixSuite) SetUpSuite(c *C) { testleak.BeforeTest() - store, err := mockstore.NewMockTikvStore() + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) s.s = store diff --git a/util/profile/profile_test.go b/util/profile/profile_test.go index d132d246add4b..6626f7dca6d91 100644 --- a/util/profile/profile_test.go +++ b/util/profile/profile_test.go @@ -34,7 +34,7 @@ var _ = Suite(&profileSuite{}) func (s *profileSuite) SetUpSuite(c *C) { var err error - s.store, err = mockstore.NewMockTikvStore() + s.store, err = mockstore.NewMockStore() c.Assert(err, IsNil) session.DisableStats4Test() s.dom, err = session.BootstrapSession(s.store) diff --git a/util/ranger/ranger_test.go b/util/ranger/ranger_test.go index d29853c5b83f9..cf2b942f08c34 100644 --- a/util/ranger/ranger_test.go +++ b/util/ranger/ranger_test.go @@ -29,7 +29,6 @@ import ( "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/ranger" @@ -61,13 +60,7 @@ func (s *testRangerSuite) TearDownSuite(c *C) { } func newDomainStoreWithBootstrap(c *C) (*domain.Domain, kv.Storage, error) { - cluster := mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(cluster) - mvccStore := mocktikv.MustNewMVCCStore() - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(cluster), - mockstore.WithMVCCStore(mvccStore), - ) + store, err := mockstore.NewMockStore() c.Assert(err, IsNil) session.SetSchemaLease(0) session.DisableStats4Test() From 4a67c3a43cbcaf78ed2fd332efa1ff3917860419 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Mon, 18 May 2020 22:16:45 +0800 Subject: [PATCH 15/74] *: fix setHeapProfileTracker panic on Windows (#17226) --- tidb-server/main.go | 2 +- util/profile/trackerRecorder.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tidb-server/main.go b/tidb-server/main.go index 325a607857c9c..e43e698193985 100644 --- a/tidb-server/main.go +++ b/tidb-server/main.go @@ -175,8 +175,8 @@ func main() { } setGlobalVars() setCPUAffinity() - setHeapProfileTracker() setupLog() + setHeapProfileTracker() setupTracing() // Should before createServer and after setup config. printInfo() setupBinlogClient() diff --git a/util/profile/trackerRecorder.go b/util/profile/trackerRecorder.go index 7d516a0bd436f..c5e5390a51bff 100644 --- a/util/profile/trackerRecorder.go +++ b/util/profile/trackerRecorder.go @@ -16,8 +16,9 @@ package profile import ( "time" + "github.com/pingcap/log" "github.com/pingcap/tidb/util/kvcache" - log "github.com/sirupsen/logrus" + "go.uber.org/zap" ) var col = &Collector{} @@ -32,7 +33,7 @@ func HeapProfileForGlobalMemTracker(d time.Duration) { case <-t.C: err := heapProfileForGlobalMemTracker() if err != nil { - log.Warnf("profile memory into tracker failed, err: %v", err) + log.Warn("profile memory into tracker failed", zap.Error(err)) } } } @@ -45,7 +46,7 @@ func heapProfileForGlobalMemTracker() error { } defer func() { if p := recover(); p != nil { - log.Warnf("GlobalLRUMemUsageTracker meet panic: %s", p) + log.Error("GlobalLRUMemUsageTracker meet panic", zap.Any("panic", p), zap.Stack("stack")) } }() kvcache.GlobalLRUMemUsageTracker.ReplaceBytesUsed(bytes) From 06f16d58d9d1fd0c2859e2140458efa3eae2d3d0 Mon Sep 17 00:00:00 2001 From: lysu Date: Tue, 19 May 2020 00:31:39 +0800 Subject: [PATCH 16/74] tikv: fix wrong "Commit Wait Token Duration" recording unit (#17268) --- store/tikv/2pc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/tikv/2pc.go b/store/tikv/2pc.go index 6f6409a758672..a5ce182fe86ff 100644 --- a/store/tikv/2pc.go +++ b/store/tikv/2pc.go @@ -1588,6 +1588,6 @@ func (batchExe *batchExecutor) process(batches []batchMutations) error { } } close(exitCh) - metrics.TiKVTokenWaitDuration.Observe(float64(batchExe.tokenWaitDuration)) + metrics.TiKVTokenWaitDuration.Observe(batchExe.tokenWaitDuration.Seconds()) return err } From 0ae08fa8f2a6b9b00c6f6bc5fb630a2b15f95d2c Mon Sep 17 00:00:00 2001 From: Feng Liyuan Date: Tue, 19 May 2020 16:32:44 +0800 Subject: [PATCH 17/74] .github: restore `What did you see instead` in bug report (#17284) --- .github/ISSUE_TEMPLATE/bug-report.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index fc687a93f4011..6eec4b803e0d9 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -14,10 +14,12 @@ Please answer these questions before submitting your issue. Thanks! ### 2. What did you expect to see? (Required) -### 3. Affected version (Required) +### 3. What did you see instead (Required) + +### 4. Affected version (Required) -### 4. Root Cause Analysis +### 5. Root Cause Analysis From 838de2ad26a1e494b7b7ed102004eaa3c5510fef Mon Sep 17 00:00:00 2001 From: wjHuang Date: Tue, 19 May 2020 18:09:44 +0800 Subject: [PATCH 18/74] executor: fix wrong behavior of set charset statement (#16984) --- executor/set.go | 33 +++++++++++++++++++++++++++------ executor/set_test.go | 2 +- go.mod | 6 +++--- go.sum | 8 ++++---- sessionctx/variable/sysvar.go | 6 ++++++ 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/executor/set.go b/executor/set.go index 0fed40596cb11..897377d59588f 100644 --- a/executor/set.go +++ b/executor/set.go @@ -55,10 +55,10 @@ func (e *SetExecutor) Next(ctx context.Context, req *chunk.Chunk) error { sessionVars := e.ctx.GetSessionVars() for _, v := range e.vars { // Variable is case insensitive, we use lower case. - if v.Name == ast.SetNames { + if v.Name == ast.SetNames || v.Name == ast.SetCharset { // This is set charset stmt. if v.IsDefault { - err := e.setCharset(mysql.DefaultCharset, "") + err := e.setCharset(mysql.DefaultCharset, "", v.Name == ast.SetNames) if err != nil { return err } @@ -73,7 +73,7 @@ func (e *SetExecutor) Next(ctx context.Context, req *chunk.Chunk) error { if v.ExtendValue != nil { co = v.ExtendValue.Value.GetString() } - err = e.setCharset(cs, co) + err = e.setCharset(cs, co, v.Name == ast.SetNames) if err != nil { return err } @@ -232,7 +232,7 @@ func (e *SetExecutor) setSysVariable(name string, v *expression.VarAssignment) e return nil } -func (e *SetExecutor) setCharset(cs, co string) error { +func (e *SetExecutor) setCharset(cs, co string, isSetName bool) error { var err error if len(co) == 0 { if co, err = charset.GetDefaultCollation(cs); err != nil { @@ -248,12 +248,33 @@ func (e *SetExecutor) setCharset(cs, co string) error { } } sessionVars := e.ctx.GetSessionVars() - for _, v := range variable.SetNamesVariables { + if isSetName { + for _, v := range variable.SetNamesVariables { + if err = sessionVars.SetSystemVar(v, cs); err != nil { + return errors.Trace(err) + } + } + return errors.Trace(sessionVars.SetSystemVar(variable.CollationConnection, co)) + } + // Set charset statement, see also https://dev.mysql.com/doc/refman/8.0/en/set-character-set.html. + for _, v := range variable.SetCharsetVariables { if err = sessionVars.SetSystemVar(v, cs); err != nil { return errors.Trace(err) } } - return sessionVars.SetSystemVar(variable.CollationConnection, co) + csDb, err := sessionVars.GlobalVarsAccessor.GetGlobalSysVar(variable.CharsetDatabase) + if err != nil { + return err + } + coDb, err := sessionVars.GlobalVarsAccessor.GetGlobalSysVar(variable.CollationDatabase) + if err != nil { + return err + } + err = sessionVars.SetSystemVar(variable.CharacterSetConnection, csDb) + if err != nil { + return errors.Trace(err) + } + return errors.Trace(sessionVars.SetSystemVar(variable.CollationConnection, coDb)) } func (e *SetExecutor) getVarValue(v *expression.VarAssignment, sysVar *variable.SysVar) (value types.Datum, err error) { diff --git a/executor/set_test.go b/executor/set_test.go index 89d0e9673d403..4a555da819eeb 100644 --- a/executor/set_test.go +++ b/executor/set_test.go @@ -499,7 +499,7 @@ func (s *testSuite5) TestSetCharset(c *C) { tk.MustExec(`SET CHARACTER SET latin1`) check( "latin1", - "latin1", + "utf8mb4", "latin1", "utf8mb4", "utf8mb4", diff --git a/go.mod b/go.mod index 33373f811296d..0638e804d70e7 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/danjacques/gofslock v0.0.0-20191023191349-0a45f885bc37 github.com/dgraph-io/ristretto v0.0.1 github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 - github.com/go-sql-driver/mysql v1.4.1 + github.com/go-sql-driver/mysql v1.5.0 github.com/gogo/protobuf v1.3.1 github.com/golang/protobuf v1.3.4 github.com/golang/snappy v0.0.1 @@ -30,8 +30,8 @@ require ( github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677 - github.com/pingcap/log v0.0.0-20200511115504-543df19646ad - github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657 + github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd + github.com/pingcap/parser v0.0.0-20200515063435-34703eeb52b3 github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible diff --git a/go.sum b/go.sum index 9eed90567acbb..4e77997acdcde 100644 --- a/go.sum +++ b/go.sum @@ -154,8 +154,9 @@ github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTM github.com/go-playground/overalls v0.0.0-20180201144345-22ec1a223b7c/go.mod h1:UqxAgEOt89sCiXlrc/ycnx00LVvUO/eS8tMUkWX4R7w= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -380,8 +381,8 @@ github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIf github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc= github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/parser v0.0.0-20200424075042-8222d8b724a4/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= -github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657 h1:2ceTso30kmgMeddZ4iZ6zrK8N9eFF8zmCa1hSSE1tXc= -github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= +github.com/pingcap/parser v0.0.0-20200515063435-34703eeb52b3 h1:zF4dhoVicadrmg5vc4ip4SuwdBc5H7kHryYxGB1EJIE= +github.com/pingcap/parser v0.0.0-20200515063435-34703eeb52b3/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 h1:JTzYYukREvxVSKW/ncrzNjFitd8snoQ/Xz32pw8i+s8= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2/go.mod h1:s+utZtXDznOiL24VK0qGmtoHjjXNsscJx3m1n8cC56s= github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= @@ -750,7 +751,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index a89949b9d03ff..4067052aeab66 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -744,6 +744,12 @@ var SetNamesVariables = []string{ "character_set_results", } +// SetCharsetVariables is the system variable names related to set charset statements. +var SetCharsetVariables = []string{ + "character_set_client", + "character_set_results", +} + const ( // CharacterSetConnection is the name for character_set_connection system variable. CharacterSetConnection = "character_set_connection" From 1982be5009c3f9e84df37e6e48d8523b81518b0c Mon Sep 17 00:00:00 2001 From: crazycs Date: Tue, 19 May 2020 19:44:23 +0800 Subject: [PATCH 19/74] executor: support show table partition regions (#17229) --- executor/executor_test.go | 77 ++++++++++++++++++++++++++++++++++++++- executor/show.go | 66 +++++++++++++++++---------------- go.mod | 2 +- go.sum | 4 +- 4 files changed, 112 insertions(+), 37 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index b21dd1ac78a5c..df24814e50fee 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -4416,16 +4416,52 @@ func (s *testSplitTable) TestShowTableRegion(c *C) { p := tbl.Meta().GetPartitionInfo().Definitions[i] c.Assert(rows[i*4+0][1], Equals, fmt.Sprintf("t_%d_", p.ID)) c.Assert(rows[i*4+1][1], Equals, fmt.Sprintf("t_%d_r_1000000", p.ID)) - c.Assert(rows[i*4+2][1], Equals, fmt.Sprintf("t_%d_r_1200000", p.ID)) c.Assert(rows[i*4+3][1], Equals, fmt.Sprintf("t_%d_r_1400000", p.ID)) c.Assert(rows[i*4+4][1], Equals, fmt.Sprintf("t_%d_r_1600000", p.ID)) c.Assert(rows[i*4+5][1], Equals, fmt.Sprintf("t_%d_r_1800000", p.ID)) - c.Assert(rows[i*4+6][1], Equals, fmt.Sprintf("t_%d_r_2000000", p.ID)) c.Assert(rows[i*4+7][1], Equals, fmt.Sprintf("t_%d_r_3000000", p.ID)) } + // Test for show table partition regions. + for i := 0; i < 4; i++ { + re = tk.MustQuery(fmt.Sprintf("show table t partition (p%v) regions", i)) + rows = re.Rows() + c.Assert(len(rows), Equals, 4) + p := tbl.Meta().GetPartitionInfo().Definitions[i] + c.Assert(rows[0][1], Equals, fmt.Sprintf("t_%d_", p.ID)) + c.Assert(rows[1][1], Equals, fmt.Sprintf("t_%d_r_1000000", p.ID)) + c.Assert(rows[2][1], Equals, fmt.Sprintf("t_%d_r_2000000", p.ID)) + c.Assert(rows[3][1], Equals, fmt.Sprintf("t_%d_r_3000000", p.ID)) + } + re = tk.MustQuery("show table t partition (p0, p4) regions") + rows = re.Rows() + c.Assert(len(rows), Equals, 12) + p := tbl.Meta().GetPartitionInfo().Definitions[0] + c.Assert(rows[0][1], Equals, fmt.Sprintf("t_%d_", p.ID)) + c.Assert(rows[1][1], Equals, fmt.Sprintf("t_%d_r_1000000", p.ID)) + c.Assert(rows[2][1], Equals, fmt.Sprintf("t_%d_r_2000000", p.ID)) + c.Assert(rows[3][1], Equals, fmt.Sprintf("t_%d_r_3000000", p.ID)) + p = tbl.Meta().GetPartitionInfo().Definitions[4] + c.Assert(rows[4][1], Equals, fmt.Sprintf("t_%d_", p.ID)) + c.Assert(rows[5][1], Equals, fmt.Sprintf("t_%d_r_1000000", p.ID)) + c.Assert(rows[6][1], Equals, fmt.Sprintf("t_%d_r_1200000", p.ID)) + c.Assert(rows[7][1], Equals, fmt.Sprintf("t_%d_r_1400000", p.ID)) + c.Assert(rows[8][1], Equals, fmt.Sprintf("t_%d_r_1600000", p.ID)) + c.Assert(rows[9][1], Equals, fmt.Sprintf("t_%d_r_1800000", p.ID)) + c.Assert(rows[10][1], Equals, fmt.Sprintf("t_%d_r_2000000", p.ID)) + c.Assert(rows[11][1], Equals, fmt.Sprintf("t_%d_r_3000000", p.ID)) + // Test for duplicate partition names. + re = tk.MustQuery("show table t partition (p0, p0, p0) regions") + rows = re.Rows() + c.Assert(len(rows), Equals, 4) + p = tbl.Meta().GetPartitionInfo().Definitions[0] + c.Assert(rows[0][1], Equals, fmt.Sprintf("t_%d_", p.ID)) + c.Assert(rows[1][1], Equals, fmt.Sprintf("t_%d_r_1000000", p.ID)) + c.Assert(rows[2][1], Equals, fmt.Sprintf("t_%d_r_2000000", p.ID)) + c.Assert(rows[3][1], Equals, fmt.Sprintf("t_%d_r_3000000", p.ID)) + // Test split partition table index. tk.MustExec("drop table if exists t") tk.MustExec("create table t (a int,b int,index idx(a)) partition by hash(a) partitions 5;") @@ -4482,6 +4518,39 @@ func (s *testSplitTable) TestShowTableRegion(c *C) { c.Assert(rows[i*8+11][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) } + // Test show table partition region on unknown-partition. + err = tk.QueryToErr("show table t partition (p_unknown) index idx regions") + c.Assert(terror.ErrorEqual(err, table.ErrUnknownPartition), IsTrue) + + // Test show table partition index. + for i := 0; i < 4; i++ { + re = tk.MustQuery(fmt.Sprintf("show table t partition (p%v) index idx regions", i)) + rows = re.Rows() + c.Assert(len(rows), Equals, 4) + p := tbl.Meta().GetPartitionInfo().Definitions[i] + c.Assert(rows[0][1], Equals, fmt.Sprintf("t_%d_", p.ID)) + c.Assert(rows[1][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[2][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[3][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + } + re = tk.MustQuery("show table t partition (p3,p4) index idx regions") + rows = re.Rows() + c.Assert(len(rows), Equals, 12) + p = tbl.Meta().GetPartitionInfo().Definitions[3] + c.Assert(rows[0][1], Equals, fmt.Sprintf("t_%d_", p.ID)) + c.Assert(rows[1][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[2][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[3][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + p = tbl.Meta().GetPartitionInfo().Definitions[4] + c.Assert(rows[4][1], Equals, fmt.Sprintf("t_%d_", p.ID)) + c.Assert(rows[5][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[6][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[7][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[8][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[9][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[10][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + c.Assert(rows[11][1], Matches, fmt.Sprintf("t_%d_i_1_.*", p.ID)) + // Test split for the second index. tk.MustExec("drop table if exists t") tk.MustExec("create table t (a int,b int,index idx(a), index idx2(b))") @@ -4494,6 +4563,10 @@ func (s *testSplitTable) TestShowTableRegion(c *C) { c.Assert(rows[1][1], Equals, fmt.Sprintf("t_%d_", tbl.Meta().ID)) c.Assert(rows[2][1], Matches, fmt.Sprintf("t_%d_i_2_.*", tbl.Meta().ID)) c.Assert(rows[3][1], Matches, fmt.Sprintf("t_%d_i_2_.*", tbl.Meta().ID)) + + // Test show table partition region on non-partition table. + err = tk.QueryToErr("show table t partition (p3,p4) index idx regions") + c.Assert(terror.ErrorEqual(err, plannercore.ErrPartitionClauseOnNonpartitioned), IsTrue) } func testGetTableByName(c *C, ctx sessionctx.Context, db, table string) table.Table { diff --git a/executor/show.go b/executor/show.go index a2eb830fc409d..76307c76476b4 100644 --- a/executor/show.go +++ b/executor/show.go @@ -51,6 +51,7 @@ import ( "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/types/json" "github.com/pingcap/tidb/util/chunk" @@ -1469,6 +1470,27 @@ func (e *ShowExec) fetchShowTableRegions() error { return errors.Trace(err) } + physicalIDs := []int64{} + if pi := tb.Meta().GetPartitionInfo(); pi != nil { + for _, name := range e.Table.PartitionNames { + pid, err := tables.FindPartitionByName(tb.Meta(), name.L) + if err != nil { + return err + } + physicalIDs = append(physicalIDs, pid) + } + if len(physicalIDs) == 0 { + for _, p := range pi.Definitions { + physicalIDs = append(physicalIDs, p.ID) + } + } + } else { + if len(e.Table.PartitionNames) != 0 { + return plannercore.ErrPartitionClauseOnNonpartitioned + } + physicalIDs = append(physicalIDs, tb.Meta().ID) + } + // Get table regions from from pd, not from regionCache, because the region cache maybe outdated. var regions []regionMeta if len(e.IndexName.L) != 0 { @@ -1476,9 +1498,9 @@ func (e *ShowExec) fetchShowTableRegions() error { if indexInfo == nil { return plannercore.ErrKeyDoesNotExist.GenWithStackByArgs(e.IndexName, tb.Meta().Name) } - regions, err = getTableIndexRegions(tb, indexInfo, tikvStore, splitStore) + regions, err = getTableIndexRegions(indexInfo, physicalIDs, tikvStore, splitStore) } else { - regions, err = getTableRegions(tb, tikvStore, splitStore) + regions, err = getTableRegions(tb, physicalIDs, tikvStore, splitStore) } if err != nil { @@ -1488,48 +1510,28 @@ func (e *ShowExec) fetchShowTableRegions() error { return nil } -func getTableRegions(tb table.Table, tikvStore tikv.Storage, splitStore kv.SplittableStore) ([]regionMeta, error) { - if info := tb.Meta().GetPartitionInfo(); info != nil { - return getPartitionTableRegions(info, tb.(table.PartitionedTable), tikvStore, splitStore) - } - return getPhysicalTableRegions(tb.Meta().ID, tb.Meta(), tikvStore, splitStore, nil) -} - -func getTableIndexRegions(tb table.Table, indexInfo *model.IndexInfo, tikvStore tikv.Storage, splitStore kv.SplittableStore) ([]regionMeta, error) { - if info := tb.Meta().GetPartitionInfo(); info != nil { - return getPartitionIndexRegions(info, tb.(table.PartitionedTable), indexInfo, tikvStore, splitStore) - } - return getPhysicalIndexRegions(tb.Meta().ID, indexInfo, tikvStore, splitStore, nil) -} - -func getPartitionTableRegions(info *model.PartitionInfo, tbl table.PartitionedTable, tikvStore tikv.Storage, splitStore kv.SplittableStore) ([]regionMeta, error) { - regions := make([]regionMeta, 0, len(info.Definitions)) +func getTableRegions(tb table.Table, physicalIDs []int64, tikvStore tikv.Storage, splitStore kv.SplittableStore) ([]regionMeta, error) { + regions := make([]regionMeta, 0, len(physicalIDs)) uniqueRegionMap := make(map[uint64]struct{}) - for _, def := range info.Definitions { - pid := def.ID - partition := tbl.GetPartition(pid) - partition.GetPhysicalID() - partitionRegions, err := getPhysicalTableRegions(partition.GetPhysicalID(), tbl.Meta(), tikvStore, splitStore, uniqueRegionMap) + for _, id := range physicalIDs { + rs, err := getPhysicalTableRegions(id, tb.Meta(), tikvStore, splitStore, uniqueRegionMap) if err != nil { return nil, err } - regions = append(regions, partitionRegions...) + regions = append(regions, rs...) } return regions, nil } -func getPartitionIndexRegions(info *model.PartitionInfo, tbl table.PartitionedTable, indexInfo *model.IndexInfo, tikvStore tikv.Storage, splitStore kv.SplittableStore) ([]regionMeta, error) { - var regions []regionMeta +func getTableIndexRegions(indexInfo *model.IndexInfo, physicalIDs []int64, tikvStore tikv.Storage, splitStore kv.SplittableStore) ([]regionMeta, error) { + regions := make([]regionMeta, 0, len(physicalIDs)) uniqueRegionMap := make(map[uint64]struct{}) - for _, def := range info.Definitions { - pid := def.ID - partition := tbl.GetPartition(pid) - partition.GetPhysicalID() - partitionRegions, err := getPhysicalIndexRegions(partition.GetPhysicalID(), indexInfo, tikvStore, splitStore, uniqueRegionMap) + for _, id := range physicalIDs { + rs, err := getPhysicalIndexRegions(id, indexInfo, tikvStore, splitStore, uniqueRegionMap) if err != nil { return nil, err } - regions = append(regions, partitionRegions...) + regions = append(regions, rs...) } return regions, nil } diff --git a/go.mod b/go.mod index 0638e804d70e7..10c9fbb57abaa 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677 github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd - github.com/pingcap/parser v0.0.0-20200515063435-34703eeb52b3 + github.com/pingcap/parser v0.0.0-20200515083134-baa47367bc23 github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible diff --git a/go.sum b/go.sum index 4e77997acdcde..d8fd40ac6fe41 100644 --- a/go.sum +++ b/go.sum @@ -381,8 +381,8 @@ github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIf github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc= github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/parser v0.0.0-20200424075042-8222d8b724a4/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= -github.com/pingcap/parser v0.0.0-20200515063435-34703eeb52b3 h1:zF4dhoVicadrmg5vc4ip4SuwdBc5H7kHryYxGB1EJIE= -github.com/pingcap/parser v0.0.0-20200515063435-34703eeb52b3/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= +github.com/pingcap/parser v0.0.0-20200515083134-baa47367bc23 h1:Ljs6GisUJJTA95ui5HQ68wgpLcCx50CJcfd5TT+Ead0= +github.com/pingcap/parser v0.0.0-20200515083134-baa47367bc23/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 h1:JTzYYukREvxVSKW/ncrzNjFitd8snoQ/Xz32pw8i+s8= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2/go.mod h1:s+utZtXDznOiL24VK0qGmtoHjjXNsscJx3m1n8cC56s= github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= From c928a53cd2d68f34978ab111ba5af5948a8d1752 Mon Sep 17 00:00:00 2001 From: AdamKorcz <44787359+AdamKorcz@users.noreply.github.com> Date: Tue, 19 May 2020 13:29:39 +0100 Subject: [PATCH 20/74] types: Introduce fuzzing by adding 3 fuzzers (#17250) * Added 3 fuzzers Co-authored-by: pingcap-github-bot --- types/fuzzMarshalJSON.go | 16 ++++++++++++++++ types/fuzzNewBitLiteral.go | 10 ++++++++++ types/fuzzNewHexLiteral.go | 10 ++++++++++ 3 files changed, 36 insertions(+) create mode 100644 types/fuzzMarshalJSON.go create mode 100644 types/fuzzNewBitLiteral.go create mode 100644 types/fuzzNewHexLiteral.go diff --git a/types/fuzzMarshalJSON.go b/types/fuzzMarshalJSON.go new file mode 100644 index 0000000000000..e2da3f2e8bb4d --- /dev/null +++ b/types/fuzzMarshalJSON.go @@ -0,0 +1,16 @@ +package types + +import "github.com/pingcap/tidb/types/json" + +// FuzzMarshalJSON implements the fuzzer +func FuzzMarshalJSON(data []byte) int { + bj, err := json.ParseBinaryFromString(string(data)) + if err != nil { + return -1 + } + _, err = bj.MarshalJSON() + if err != nil { + return 0 + } + return 1 +} diff --git a/types/fuzzNewBitLiteral.go b/types/fuzzNewBitLiteral.go new file mode 100644 index 0000000000000..2790f4ca0f7a7 --- /dev/null +++ b/types/fuzzNewBitLiteral.go @@ -0,0 +1,10 @@ +package types + +// FuzzNewBitLiteral implements the fuzzer +func FuzzNewBitLiteral(data []byte) int { + _, err := NewBitLiteral(string(data)) + if err != nil { + return 0 + } + return 1 +} diff --git a/types/fuzzNewHexLiteral.go b/types/fuzzNewHexLiteral.go new file mode 100644 index 0000000000000..a68308d06e90d --- /dev/null +++ b/types/fuzzNewHexLiteral.go @@ -0,0 +1,10 @@ +package types + +// FuzzNewHexLiteral implements the fuzzer +func FuzzNewHexLiteral(data []byte) int { + _, err := NewHexLiteral(string(data)) + if err != nil { + return 0 + } + return 1 +} From 997b986387283d68713f50a3cdf5d44abbfccb5a Mon Sep 17 00:00:00 2001 From: wjHuang Date: Tue, 19 May 2020 21:18:24 +0800 Subject: [PATCH 21/74] session: fix a bug that upgrading from 3.1.1 to 4.0 fails (#17293) --- session/bootstrap.go | 15 +++++++++++++++ session/session.go | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/session/bootstrap.go b/session/bootstrap.go index 67b1a285c07a7..b32e7de3b4be8 100644 --- a/session/bootstrap.go +++ b/session/bootstrap.go @@ -384,6 +384,8 @@ const ( version44 = 44 // version45 introduces CONFIG_PRIV for SET CONFIG statements. version45 = 45 + // version46 fix a bug in v3.1.1. + version46 = 46 ) var ( @@ -432,6 +434,7 @@ var ( upgradeToVer43, upgradeToVer44, upgradeToVer45, + upgradeToVer46, } ) @@ -1045,6 +1048,18 @@ func upgradeToVer45(s Session, ver int64) { mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Config_priv='Y' where Super_priv='Y'") } +// In v3.1.1, we wrongly replace the context of upgradeToVer39 with upgradeToVer44. If we upgrade from v3.1.1 to a newer version, +// upgradeToVer39 will be missed. So we redo upgradeToVer39 here to make sure the upgrading from v3.1.1 succeed. +func upgradeToVer46(s Session, ver int64) { + if ver >= version46 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Reload_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `File_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Reload_priv='Y' where Super_priv='Y'") + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET File_priv='Y' where Super_priv='Y'") +} + // updateBootstrapVer updates bootstrap version variable in mysql.TiDB table. func updateBootstrapVer(s Session) { // Update bootstrap version. diff --git a/session/session.go b/session/session.go index 86f233e5f584e..56a3af7cac15a 100644 --- a/session/session.go +++ b/session/session.go @@ -1920,7 +1920,7 @@ func CreateSessionWithDomain(store kv.Storage, dom *domain.Domain) (*session, er const ( notBootstrapped = 0 - currentBootstrapVersion = version45 + currentBootstrapVersion = version46 ) func getStoreBootstrapVersion(store kv.Storage) int64 { From c60ea27e6ca6f09c466c67deea8e102e8e3af9ee Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Wed, 20 May 2020 13:05:44 +0800 Subject: [PATCH 22/74] *: fix partition selection for the update statement (#17285) --- executor/builder.go | 14 ++++++- executor/executor_test.go | 58 ++++++++++++++++++++++++++++ planner/core/common_plans.go | 4 ++ planner/core/logical_plan_builder.go | 17 +++++--- table/table.go | 2 + table/tables/partition.go | 46 ++++++++++++++++++++++ 6 files changed, 135 insertions(+), 6 deletions(-) diff --git a/executor/builder.go b/executor/builder.go index 207797d4e5264..2479aa6777f95 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -1643,7 +1643,19 @@ func (b *executorBuilder) buildSplitRegion(v *plannercore.SplitRegion) Executor func (b *executorBuilder) buildUpdate(v *plannercore.Update) Executor { tblID2table := make(map[int64]table.Table, len(v.TblColPosInfos)) for _, info := range v.TblColPosInfos { - tblID2table[info.TblID], _ = b.is.TableByID(info.TblID) + tbl, _ := b.is.TableByID(info.TblID) + tblID2table[info.TblID] = tbl + if len(v.PartitionedTable) > 0 { + // The v.PartitionedTable collects the partitioned table. + // Replace the original table with the partitioned table to support partition selection. + // e.g. update t partition (p0, p1), the new values are not belong to the given set p0, p1 + // Using the table in v.PartitionedTable returns a proper error, while using the original table can't. + for _, p := range v.PartitionedTable { + if info.TblID == p.Meta().ID { + tblID2table[info.TblID] = p + } + } + } } if b.err = b.updateForUpdateTSIfNeeded(v.SelectPlan); b.err != nil { return nil diff --git a/executor/executor_test.go b/executor/executor_test.go index df24814e50fee..b9839b9bcbd48 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -5634,3 +5634,61 @@ func (s *testSuite1) TestInsertValuesWithSubQuery(c *C) { tk.MustExec("insert into t2 set a = 3, b = 5, c = b") tk.MustQuery("select * from t2").Check(testkit.Rows("2 4 2", "3 5 5")) } + +func (s *testSuite1) TestUpdateGivenPartitionSet(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1,t2,t3") + tk.MustExec(`create table t1( + a int(11), + b varchar(10) DEFAULT NULL, + primary key idx_a (a)) PARTITION BY RANGE (a) + (PARTITION p0 VALUES LESS THAN (10) ENGINE = InnoDB, + PARTITION p1 VALUES LESS THAN (20) ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN (30) ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN (40) ENGINE = InnoDB, + PARTITION p4 VALUES LESS THAN MAXVALUE ENGINE = InnoDB)`) + + tk.MustExec(`create table t2( + a int(11) DEFAULT NULL, + b varchar(10) DEFAULT NULL) PARTITION BY RANGE (a) + (PARTITION p0 VALUES LESS THAN (10) ENGINE = InnoDB, + PARTITION p1 VALUES LESS THAN (20) ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN (30) ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN (40) ENGINE = InnoDB, + PARTITION p4 VALUES LESS THAN MAXVALUE ENGINE = InnoDB)`) + + tk.MustExec(`create table t3 (a int(11), b varchar(10) default null)`) + + defer tk.MustExec("drop table if exists t1,t2,t3") + tk.MustExec("insert into t3 values(1, 'a'), (2, 'b'), (11, 'c'), (21, 'd')") + err := tk.ExecToErr("update t3 partition(p0) set a = 40 where a = 2") + c.Assert(err.Error(), Equals, "[planner:1747]PARTITION () clause on non partitioned table") + + // update with primary key change + tk.MustExec("insert into t1 values(1, 'a'), (2, 'b'), (11, 'c'), (21, 'd')") + err = tk.ExecToErr("update t1 partition(p0, p1) set a = 40") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + err = tk.ExecToErr("update t1 partition(p0) set a = 40 where a = 2") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + // test non-exist partition. + err = tk.ExecToErr("update t1 partition (p0, p_non_exist) set a = 40") + c.Assert(err.Error(), Equals, "[table:1735]Unknown partition 'p_non_exist' in table 't1'") + // test join. + err = tk.ExecToErr("update t1 partition (p0), t3 set t1.a = 40 where t3.a = 2") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + + tk.MustExec("update t1 partition(p0) set a = 3 where a = 2") + tk.MustExec("update t1 partition(p0, p3) set a = 33 where a = 1") + + // update without partition change + tk.MustExec("insert into t2 values(1, 'a'), (2, 'b'), (11, 'c'), (21, 'd')") + err = tk.ExecToErr("update t2 partition(p0, p1) set a = 40") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + err = tk.ExecToErr("update t2 partition(p0) set a = 40 where a = 2") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + + tk.MustExec("update t2 partition(p0) set a = 3 where a = 2") + tk.MustExec("update t2 partition(p0, p3) set a = 33 where a = 1") + +} diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index cf45c46da260a..d06288ba66eed 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -657,6 +657,10 @@ type Update struct { SelectPlan PhysicalPlan TblColPosInfos TblColPosInfoSlice + + // Used when partition sets are given. + // e.g. update t partition(p0) set a = 1; + PartitionedTable []table.PartitionedTable } // Delete represents a delete plan. diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index d0357d93370c7..3b135e7865cb4 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -2688,14 +2688,20 @@ func (b *PlanBuilder) buildDataSource(ctx context.Context, tn *ast.TableName, as if tableInfo.GetPartitionInfo() != nil { b.optFlag = b.optFlag | flagPartitionProcessor - b.partitionedTable = append(b.partitionedTable, tbl.(table.PartitionedTable)) + pt := tbl.(table.PartitionedTable) // check partition by name. - for _, name := range tn.PartitionNames { - _, err = tables.FindPartitionByName(tableInfo, name.L) - if err != nil { - return nil, err + if len(tn.PartitionNames) > 0 { + pids := make(map[int64]struct{}, len(tn.PartitionNames)) + for _, name := range tn.PartitionNames { + pid, err := tables.FindPartitionByName(tableInfo, name.L) + if err != nil { + return nil, err + } + pids[pid] = struct{}{} } + pt = tables.NewPartitionTableithGivenSets(pt, pids) } + b.partitionedTable = append(b.partitionedTable, pt) } else if len(tn.PartitionNames) != 0 { return nil, ErrPartitionClauseOnNonpartitioned } @@ -3374,6 +3380,7 @@ func (b *PlanBuilder) buildUpdate(ctx context.Context, update *ast.UpdateStmt) ( if err == nil { err = checkUpdateList(b.ctx, tblID2table, updt) } + updt.PartitionedTable = b.partitionedTable return updt, err } diff --git a/table/table.go b/table/table.go index 072184e944115..f15d5665fa22e 100644 --- a/table/table.go +++ b/table/table.go @@ -102,6 +102,8 @@ var ( ErrLockOrActiveTransaction = terror.ClassTable.New(mysql.ErrLockOrActiveTransaction, mysql.MySQLErrName[mysql.ErrLockOrActiveTransaction]) // ErrSequenceHasRunOut returns when sequence has run out. ErrSequenceHasRunOut = terror.ClassTable.New(mysql.ErrSequenceRunOut, mysql.MySQLErrName[mysql.ErrSequenceRunOut]) + // ErrRowDoesNotMatchGivenPartitionSet returns when the destination partition conflict with the partition selection. + ErrRowDoesNotMatchGivenPartitionSet = terror.ClassTable.NewStd(mysql.ErrRowDoesNotMatchGivenPartitionSet) ) // RecordIterFunc is used for low-level record iteration. diff --git a/table/tables/partition.go b/table/tables/partition.go index eac6a82768486..88c4d300c56bd 100644 --- a/table/tables/partition.go +++ b/table/tables/partition.go @@ -344,16 +344,49 @@ func (t *partitionedTable) GetPartitionByRow(ctx sessionctx.Context, r []types.D // AddRecord implements the AddRecord method for the table.Table interface. func (t *partitionedTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + return partitionedTableAddRecord(ctx, t, r, nil, opts) +} + +func partitionedTableAddRecord(ctx sessionctx.Context, t *partitionedTable, r []types.Datum, partitionSelection map[int64]struct{}, opts []table.AddRecordOption) (recordID kv.Handle, err error) { partitionInfo := t.meta.GetPartitionInfo() pid, err := t.locatePartition(ctx, partitionInfo, r) if err != nil { return nil, errors.Trace(err) } + if partitionSelection != nil { + if _, ok := partitionSelection[pid]; !ok { + return nil, errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) + } + } tbl := t.GetPartition(pid) return tbl.AddRecord(ctx, r, opts...) } +// partitionTableWithGivenSets is used for this kind of grammar: partition (p0,p1) +// Basically it is the same as partitionedTable except that partitionTableWithGivenSets +// checks the given partition set for AddRecord/UpdateRecord operations. +type partitionTableWithGivenSets struct { + *partitionedTable + partitions map[int64]struct{} +} + +// NewPartitionTableithGivenSets creates a new partition table from a partition table. +func NewPartitionTableithGivenSets(tbl table.PartitionedTable, partitions map[int64]struct{}) table.PartitionedTable { + if raw, ok := tbl.(*partitionedTable); ok { + return &partitionTableWithGivenSets{ + partitionedTable: raw, + partitions: partitions, + } + } + return tbl +} + +// AddRecord implements the AddRecord method for the table.Table interface. +func (t *partitionTableWithGivenSets) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + return partitionedTableAddRecord(ctx, t.partitionedTable, r, t.partitions, opts) +} + // RemoveRecord implements table.Table RemoveRecord interface. func (t *partitionedTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { partitionInfo := t.meta.GetPartitionInfo() @@ -370,6 +403,14 @@ func (t *partitionedTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r [ // `touched` means which columns are really modified, used for secondary indices. // Length of `oldData` and `newData` equals to length of `t.WritableCols()`. func (t *partitionedTable) UpdateRecord(ctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error { + return partitionedTableUpdateRecord(ctx, t, h, currData, newData, touched, nil) +} + +func (t *partitionTableWithGivenSets) UpdateRecord(ctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error { + return partitionedTableUpdateRecord(ctx, t.partitionedTable, h, currData, newData, touched, t.partitions) +} + +func partitionedTableUpdateRecord(ctx sessionctx.Context, t *partitionedTable, h kv.Handle, currData, newData []types.Datum, touched []bool, partitionSelection map[int64]struct{}) error { partitionInfo := t.meta.GetPartitionInfo() from, err := t.locatePartition(ctx, partitionInfo, currData) if err != nil { @@ -379,6 +420,11 @@ func (t *partitionedTable) UpdateRecord(ctx sessionctx.Context, h kv.Handle, cur if err != nil { return errors.Trace(err) } + if partitionSelection != nil { + if _, ok := partitionSelection[to]; !ok { + return errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) + } + } // The old and new data locate in different partitions. // Remove record from old partition and add record to new partition. From 3176b00c0238dae9a455641a2ad526a42ffeca4e Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Wed, 20 May 2020 13:55:18 +0800 Subject: [PATCH 23/74] executor: fix the issue that UNIQUE constraint on boolean column results in an incorrect result in a comparison (#17245) --- executor/batch_checker.go | 2 +- executor/distsql.go | 2 +- executor/executor.go | 2 +- executor/insert.go | 2 +- executor/insert_common.go | 16 ++++++++-------- executor/point_get.go | 5 ++++- executor/point_get_test.go | 13 +++++++++++++ executor/union_scan.go | 2 +- executor/update.go | 4 ++-- executor/write.go | 2 +- table/column.go | 16 ++++++++++------ table/column_test.go | 10 +++++----- util/rowDecoder/decoder.go | 2 +- 13 files changed, 49 insertions(+), 29 deletions(-) diff --git a/executor/batch_checker.go b/executor/batch_checker.go index 006e6ce902fae..e29f85d10cd46 100644 --- a/executor/batch_checker.go +++ b/executor/batch_checker.go @@ -198,7 +198,7 @@ func getOldRow(ctx context.Context, sctx sessionctx.Context, txn kv.Transaction, if err != nil { return nil, err } - oldRow[col.Offset], err = table.CastValue(sctx, val, col.ToInfo(), false) + oldRow[col.Offset], err = table.CastValue(sctx, val, col.ToInfo(), false, false) if err != nil { return nil, err } diff --git a/executor/distsql.go b/executor/distsql.go index 9f69b91706749..ec9e7364e45ea 100644 --- a/executor/distsql.go +++ b/executor/distsql.go @@ -868,7 +868,7 @@ func (w *tableWorker) compareData(ctx context.Context, task *lookupTableTask, ta if err != nil { return errors.Trace(err) } - val, err = table.CastValue(tblReaderExec.ctx, val, col.ColumnInfo, false) + val, err = table.CastValue(tblReaderExec.ctx, val, col.ColumnInfo, false, false) if err != nil { return errors.Trace(err) } diff --git a/executor/executor.go b/executor/executor.go index e685bbd5921b9..230035a9fec7c 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -1760,7 +1760,7 @@ func FillVirtualColumnValue(virtualRetTypes []*types.FieldType, virtualColumnInd } // Because the expression might return different type from // the generated column, we should wrap a CAST on the result. - castDatum, err := table.CastValue(sctx, datum, columns[idx], true) + castDatum, err := table.CastValue(sctx, datum, columns[idx], false, true) if err != nil { return err } diff --git a/executor/insert.go b/executor/insert.go index 416917909d5ff..439a4726efce9 100644 --- a/executor/insert.go +++ b/executor/insert.go @@ -333,7 +333,7 @@ func (e *InsertExec) doDupRowUpdate(ctx context.Context, handle kv.Handle, oldRo if err1 != nil { return err1 } - e.row4Update[col.Col.Index], err1 = table.CastValue(e.ctx, val, col.Col.ToInfo(), false) + e.row4Update[col.Col.Index], err1 = table.CastValue(e.ctx, val, col.Col.ToInfo(), false, false) if err1 != nil { return err1 } diff --git a/executor/insert_common.go b/executor/insert_common.go index 8d7636888f0ec..0273efbb9baad 100644 --- a/executor/insert_common.go +++ b/executor/insert_common.go @@ -320,7 +320,7 @@ func (e *InsertValues) evalRow(ctx context.Context, list []expression.Expression if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } - val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo(), false) + val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo(), false, false) if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } @@ -349,7 +349,7 @@ func (e *InsertValues) fastEvalRow(ctx context.Context, list []expression.Expres if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } - val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo(), false) + val1, err := table.CastValue(e.ctx, val, e.insertColumns[i].ToInfo(), false, false) if err = e.handleErr(e.insertColumns[i], &val, rowIdx, err); err != nil { return nil, err } @@ -473,7 +473,7 @@ func (e *InsertValues) getRow(ctx context.Context, vals []types.Datum) ([]types. row := make([]types.Datum, len(e.Table.Cols())) hasValue := make([]bool, len(e.Table.Cols())) for i, v := range vals { - casted, err := table.CastValue(e.ctx, v, e.insertColumns[i].ToInfo(), false) + casted, err := table.CastValue(e.ctx, v, e.insertColumns[i].ToInfo(), false, false) if e.handleErr(nil, &v, 0, err) != nil { return nil, err } @@ -576,7 +576,7 @@ func (e *InsertValues) fillRow(ctx context.Context, row []types.Datum, hasValue if e.handleErr(gCol, &val, 0, err) != nil { return nil, err } - row[colIdx], err = table.CastValue(e.ctx, val, gCol.ToInfo(), false) + row[colIdx], err = table.CastValue(e.ctx, val, gCol.ToInfo(), false, false) if err != nil { return nil, err } @@ -723,7 +723,7 @@ func (e *InsertValues) lazyAdjustAutoIncrementDatum(ctx context.Context, rows [] retryInfo.AddAutoIncrementID(id) // The value of d is adjusted by auto ID, so we need to cast it again. - d, err := table.CastValue(e.ctx, d, col.ToInfo(), false) + d, err := table.CastValue(e.ctx, d, col.ToInfo(), false, false) if err != nil { return nil, err } @@ -736,7 +736,7 @@ func (e *InsertValues) lazyAdjustAutoIncrementDatum(ctx context.Context, rows [] retryInfo.AddAutoIncrementID(recordID) // the value of d is adjusted by auto ID, so we need to cast it again. - autoDatum, err = table.CastValue(e.ctx, autoDatum, col.ToInfo(), false) + autoDatum, err = table.CastValue(e.ctx, autoDatum, col.ToInfo(), false, false) if err != nil { return nil, err } @@ -796,7 +796,7 @@ func (e *InsertValues) adjustAutoIncrementDatum(ctx context.Context, d types.Dat retryInfo.AddAutoIncrementID(recordID) // the value of d is adjusted by auto ID, so we need to cast it again. - casted, err := table.CastValue(e.ctx, d, c.ToInfo(), false) + casted, err := table.CastValue(e.ctx, d, c.ToInfo(), false, false) if err != nil { return types.Datum{}, err } @@ -878,7 +878,7 @@ func (e *InsertValues) adjustAutoRandomDatum(ctx context.Context, d types.Datum, d.SetAutoID(recordID, c.Flag) retryInfo.AddAutoRandomID(recordID) - casted, err := table.CastValue(e.ctx, d, c.ToInfo(), false) + casted, err := table.CastValue(e.ctx, d, c.ToInfo(), false, false) if err != nil { return types.Datum{}, err } diff --git a/executor/point_get.go b/executor/point_get.go index 32d137eb26ef3..4d2b2c1e838e8 100644 --- a/executor/point_get.go +++ b/executor/point_get.go @@ -313,7 +313,10 @@ func encodeIndexKey(e *baseExecutor, tblInfo *model.TableInfo, idxInfo *model.In str, err = idxVals[i].ToString() idxVals[i].SetString(str, colInfo.FieldType.Collate) } else { - idxVals[i], err = table.CastValue(e.ctx, idxVals[i], colInfo, false) + idxVals[i], err = table.CastValue(e.ctx, idxVals[i], colInfo, true, false) + if types.ErrOverflow.Equal(err) { + return nil, kv.ErrNotExist + } } if err != nil { return nil, err diff --git a/executor/point_get_test.go b/executor/point_get_test.go index 7bb270ebdbff7..adb92087f3671 100644 --- a/executor/point_get_test.go +++ b/executor/point_get_test.go @@ -112,6 +112,19 @@ func (s *testPointGetSuite) TestPointGet(c *C) { " ")) } +func (s *testPointGetSuite) TestPointGetOverflow(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t0") + tk.MustExec("CREATE TABLE t0(c1 BOOL UNIQUE)") + tk.MustExec("INSERT INTO t0(c1) VALUES (-128)") + tk.MustExec("INSERT INTO t0(c1) VALUES (127)") + tk.MustQuery("SELECT t0.c1 FROM t0 WHERE t0.c1=-129").Check(testkit.Rows()) // no result + tk.MustQuery("SELECT t0.c1 FROM t0 WHERE t0.c1=-128").Check(testkit.Rows("-128")) + tk.MustQuery("SELECT t0.c1 FROM t0 WHERE t0.c1=128").Check(testkit.Rows()) + tk.MustQuery("SELECT t0.c1 FROM t0 WHERE t0.c1=127").Check(testkit.Rows("127")) +} + func (s *testPointGetSuite) TestPointGetCharPK(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec(`use test;`) diff --git a/executor/union_scan.go b/executor/union_scan.go index aae5708e48b5c..2c86ea415fa0d 100644 --- a/executor/union_scan.go +++ b/executor/union_scan.go @@ -175,7 +175,7 @@ func (us *UnionScanExec) Next(ctx context.Context, req *chunk.Chunk) error { } // Because the expression might return different type from // the generated column, we should wrap a CAST on the result. - castDatum, err := table.CastValue(us.ctx, datum, us.columns[idx], true) + castDatum, err := table.CastValue(us.ctx, datum, us.columns[idx], false, true) if err != nil { return err } diff --git a/executor/update.go b/executor/update.go index 91ddd4fbae46a..bafceef1693ee 100644 --- a/executor/update.go +++ b/executor/update.go @@ -217,7 +217,7 @@ func (e *UpdateExec) fastComposeNewRow(rowIdx int, oldRow []types.Datum, cols [] // info of `_tidb_rowid` column is nil. // No need to cast `_tidb_rowid` column value. if cols[assign.Col.Index] != nil { - val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo, false) + val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo, false, false) if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { return nil, err } @@ -244,7 +244,7 @@ func (e *UpdateExec) composeNewRow(rowIdx int, oldRow []types.Datum, cols []*tab // info of `_tidb_rowid` column is nil. // No need to cast `_tidb_rowid` column value. if cols[assign.Col.Index] != nil { - val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo, false) + val, err = table.CastValue(e.ctx, val, cols[assign.Col.Index].ColumnInfo, false, false) if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { return nil, err } diff --git a/executor/write.go b/executor/write.go index 76b8c4a5e42d4..3c512accd877f 100644 --- a/executor/write.go +++ b/executor/write.go @@ -73,7 +73,7 @@ func updateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, old for i, col := range t.Cols() { if modified[i] { // Cast changed fields with respective columns. - v, err := table.CastValue(sctx, newData[i], col.ToInfo(), false) + v, err := table.CastValue(sctx, newData[i], col.ToInfo(), false, false) if err != nil { return false, err } diff --git a/table/column.go b/table/column.go index 307e4c36ef0cd..909f673e7512d 100644 --- a/table/column.go +++ b/table/column.go @@ -143,7 +143,7 @@ func CastValues(ctx sessionctx.Context, rec []types.Datum, cols []*Column) (err sc := ctx.GetSessionVars().StmtCtx for _, c := range cols { var converted types.Datum - converted, err = CastValue(ctx, rec[c.Offset], c.ToInfo(), false) + converted, err = CastValue(ctx, rec[c.Offset], c.ToInfo(), false, false) if err != nil { if sc.DupKeyAsWarning { sc.AppendWarning(err) @@ -168,15 +168,19 @@ func handleWrongUtf8Value(ctx sessionctx.Context, col *model.ColumnInfo, casted } // CastValue casts a value based on column type. -// If forceIgnoreTruncate is true, the err returned will be always nil. +// If forceIgnoreTruncate is true, truncated errors will be ignored. +// If returnOverflow is true, don't handle overflow errors in this function. // It's safe now and it's the same as the behavior of select statement. // Set it to true only in FillVirtualColumnValue and UnionScanExec.Next() // If the handle of err is changed latter, the behavior of forceIgnoreTruncate also need to change. // TODO: change the third arg to TypeField. Not pass ColumnInfo. -func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo, forceIgnoreTruncate bool) (casted types.Datum, err error) { +func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo, returnOverflow, forceIgnoreTruncate bool) (casted types.Datum, err error) { sc := ctx.GetSessionVars().StmtCtx casted, err = val.ConvertTo(sc, &col.FieldType) // TODO: make sure all truncate errors are handled by ConvertTo. + if types.ErrOverflow.Equal(err) && returnOverflow { + return casted, err + } if types.ErrTruncated.Equal(err) { str, err1 := val.ToString() if err1 != nil { @@ -398,7 +402,7 @@ func EvalColDefaultExpr(ctx sessionctx.Context, col *model.ColumnInfo, defaultEx return types.Datum{}, err } // Check the evaluated data type by cast. - value, err := CastValue(ctx, d, col, false) + value, err := CastValue(ctx, d, col, false, false) if err != nil { return types.Datum{}, err } @@ -417,7 +421,7 @@ func getColDefaultExprValue(ctx sessionctx.Context, col *model.ColumnInfo, defau return types.Datum{}, err } // Check the evaluated data type by cast. - value, err := CastValue(ctx, d, col, false) + value, err := CastValue(ctx, d, col, false, false) if err != nil { return types.Datum{}, err } @@ -430,7 +434,7 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa } if col.Tp != mysql.TypeTimestamp && col.Tp != mysql.TypeDatetime { - value, err := CastValue(ctx, types.NewDatum(defaultVal), col, false) + value, err := CastValue(ctx, types.NewDatum(defaultVal), col, false, false) if err != nil { return types.Datum{}, err } diff --git a/table/column_test.go b/table/column_test.go index 904d12f86fff8..feffb801ebf6a 100644 --- a/table/column_test.go +++ b/table/column_test.go @@ -254,11 +254,11 @@ func (t *testTableSuite) TestCastValue(c *C) { State: model.StatePublic, } colInfo.Charset = mysql.UTF8Charset - val, err := CastValue(ctx, types.Datum{}, &colInfo, false) + val, err := CastValue(ctx, types.Datum{}, &colInfo, false, false) c.Assert(err, Equals, nil) c.Assert(val.GetInt64(), Equals, int64(0)) - val, err = CastValue(ctx, types.NewDatum("test"), &colInfo, false) + val, err = CastValue(ctx, types.NewDatum("test"), &colInfo, false, false) c.Assert(err, Not(Equals), nil) c.Assert(val.GetInt64(), Equals, int64(0)) @@ -278,16 +278,16 @@ func (t *testTableSuite) TestCastValue(c *C) { FieldType: *types.NewFieldType(mysql.TypeString), State: model.StatePublic, } - val, err = CastValue(ctx, types.NewDatum("test"), &colInfoS, false) + val, err = CastValue(ctx, types.NewDatum("test"), &colInfoS, false, false) c.Assert(err, IsNil) c.Assert(val, NotNil) colInfoS.Charset = mysql.UTF8Charset - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS, false) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS, false, false) c.Assert(err, NotNil) colInfoS.Charset = mysql.UTF8MB4Charset - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS, false) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS, false, false) c.Assert(err, NotNil) } diff --git a/util/rowDecoder/decoder.go b/util/rowDecoder/decoder.go index 5bc8e9e502aab..aec6cfb8b7df3 100644 --- a/util/rowDecoder/decoder.go +++ b/util/rowDecoder/decoder.go @@ -118,7 +118,7 @@ func (rd *RowDecoder) DecodeAndEvalRowWithMap(ctx sessionctx.Context, handle kv. if err != nil { return nil, err } - val, err = table.CastValue(ctx, val, col.Col.ColumnInfo, false) + val, err = table.CastValue(ctx, val, col.Col.ColumnInfo, false, false) if err != nil { return nil, err } From 1227d3772d53360d7b1496bc584d9e86598a1f4c Mon Sep 17 00:00:00 2001 From: SIGSEGV Date: Wed, 20 May 2020 14:24:44 +0800 Subject: [PATCH 24/74] proposal: standardize error codes and messages (#17042) --- ...08-standardize-error-codes-and-messages.md | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 docs/design/2020-05-08-standardize-error-codes-and-messages.md diff --git a/docs/design/2020-05-08-standardize-error-codes-and-messages.md b/docs/design/2020-05-08-standardize-error-codes-and-messages.md new file mode 100644 index 0000000000000..8aae92d441c83 --- /dev/null +++ b/docs/design/2020-05-08-standardize-error-codes-and-messages.md @@ -0,0 +1,208 @@ +# Proposal: Standardize error codes and messages + +- Author(s): Joshua +- Last updated: May 8 +- Discussion at: https://docs.google.com/document/d/1beoa5xyuToboSx6e6J02tjLLX5SWzmqvqHuAPEk-I58/edit?usp=sharing + +## Abstract + +This issue proposes that TiDB components maintain standard error codes and error messages according to a consistent specification. + +## Motivation + +When certain errors occur in TiDB components, users are often unaware of the meaning of the corresponding error message, and we plan the following two initiatives to alleviate this problem + +- Specification of error codes for TiDB components +- Provide a program that allows users to quickly access information corresponding to error codes + +## Proposal + +### The Metafiles + +In order to let TiUP know the the errors every component may throw, the components developers should +keep a metafile in the code repository. The metafile should be a toml file which looks like: + +```toml +[error.8005] +error = '''Write Conflict, txnStartTS is stale''' +message = '''Transactions in TiDB encounter write conflicts.''' +workaround = ''' +Check whether `tidb_disable_txn_auto_retry` is set to `on`. If so, set it to `off`; if it is already `off`, increase the value of `tidb_retry_limit` until the error no longer occurs. +''' + +[error.9005] +error = '''Region is unavailable''' +message = ''' +A certain Raft Group is not available, such as the number of replicas is not enough. +This error usually occurs when the TiKV server is busy or the TiKV node is down. +''' +workaround = '''Check the status, monitoring data and log of the TiKV server.''' +``` + +Benefit: +- It's easy to write. +- It handles multiple lines well. + +Tradeoff: +- The markdown content can't be previewed on writing. + +Except toml, we also considered json and markdown format, see `rationale` section. + +### Rationale + +This section introduce two candidate formats, which is deprecated. + +#### metafile in JSON + +The json format of metafile is like: + +```json +[ + { + "code": 8005, + "error": "Write Conflict, txnStartTS is stale", + "message": "Transactions in TiDB encounter write conflicts.", + "workaround": "Check whether `tidb_disable_txn_auto_retry` is set to `on`. If so, set it to `off`; if it is already `off`, increase the value of `tidb_retry_limit` until the error no longer occurs." + }, + { + "code": 9005, + "error": "Region is unavailable", + "message": "A certain Raft Group is not available, such as the number of replicas is not enough.\nThis error usually occurs when the TiKV server is busy or the TiKV node is down.", + "workaround": "Check the status, monitoring data and log of the TiKV server." + } +] +``` + +Benefit: +- It's easy to write (every developer knows json). +- It's easy to parse (every programming language can handle json). + +Tradeoff: +- Poor readability when multiple lines are involved. +- The markdown content can't be previewed on writing. + +#### metafile in markdown + +```md +## Code: 8005 +### Error +Write Conflict, txnStartTS is stale +### Message +A certain Raft Group is not available, such as the number of replicas is not enough. +This error usually occurs when the TiKV server is busy or the TiKV node is down. +### Workaround +Check whether `tidb_disable_txn_auto_retry` is set to `on`. If so, set it to `off`; if it is already `off`, increase the value of `tidb_retry_limit` until the error no longer occurs. + +## Code: 9005 +### Error +Region is unavailable +### Message +A certain Raft Group is not available, such as the number of replicas is not enough. +This error usually occurs when the TiKV server is busy or the TiKV node is down. +### Workaround +Check the status, monitoring data and log of the TiKV server. +``` + +Benefit: +- It's easy to write. +- It handles multiple lines well. +- The markdown content can be previewed on writing. + +Tradeoff: +- It's hard to implement (see example below) +- It's not self-consistent (see example below) + +Tradeoff Example: + +```md +## Code: 8005 +### Error +Write Conflict, txnStartTS is stale +### Message +A certain Raft Group is not available, such as the number of replicas is not enough. +This error usually occurs when the TiKV server is busy or the TiKV node is down. +### Workaround +## Code: 9005 +### Error +Region is unavailable +### Message +A certain Raft Group is not available, such as the number of replicas is not enough. +This error usually occurs when the TiKV server is busy or the TiKV node is down. +### Workaround +Check the status, monitoring data and log of the TiKV server. +``` + +As the syntax above, the 9005 block is the message part of 8005 block, so we expect it's result is the same as this toml: + +```toml +[error.8005] +error = '''Write Conflict, txnStartTS is stale''' +message = '''Transactions in TiDB encounter write conflicts.''' +workaround = ''' +## Code: 9005 +### Error +Region is unavailable +### Message +A certain Raft Group is not available, such as the number of replicas is not enough. +This error usually occurs when the TiKV server is busy or the TiKV node is down. +### Workaround +Check the status, monitoring data and log of the TiKV server. +''' +``` + +In this case, we must define the grammar and write a parser. Writing parser increase much complexity. What's worse +is that I found no grammar that fits it. + +#### Summary + +Through the above discussion, we recommend the toml version of metafile. + +In addition, the error codes should be append only in case of conflict between versions. + +### The Error Definition + +In the discussion above, an error has at least 4 parts: +- The error code: it's the identity of an error +- The error field: it's the error itself the user can view in TiDB system +- the message field: the description of the error, what happened and why happend? +- the workaround filed: how to workaround this error + +Besides, we can append a optional tags field to it: +```toml +[error.9005] +error = "" +message = "" +workaround = "" +tags = ["tikv"] +``` + +The tags is used to classify errors (e.g. the level of seriousness). At the very beginning, we can ignore it since we don't have enough errors listed. Once we have enough data, we need to classify all errors by different dimensions. Then we will make out a standard abount how to classify errors. + +#### The Error Code Range + +For compatibility with MySQL protocol, the code transmitted through the mysql protocol should be number only, others can be a number with a prefix string. + +The code of each components looks like: +- TiDB: [0, 9000), DB-([A-Z]+-)?[0-9]{3,} +- TiKV: [9000, 9010), KV-([A-Z]+-)?[0-9]{3,} +- PD: [9000, 9010), PD-([A-Z]+-)?[0-9]{3,} +- DM: DM-([A-Z]+-)?[0-9]{3,} +- BR: BR-([A-Z]+-)?[0-9]{3,} +- CDC: CDC-([A-Z]+-)?[0-9]{3,} +- Lightning: LN-([A-Z]+-)?[0-9]{3,} +- Dumpling: DP-([A-Z]+-)?[0-9]{3,} + +### How It Works + +In every build, the pipeline should fetch all these metafiles from all repositories: + +```bash +mkdir -p errors +curl https://raw.githubusercontent.com/pingcap/tidb/master/errors.toml -o errors/tidb.toml +curl https://raw.githubusercontent.com/tikv/tikv/master/errors.toml -o errors/tikv.toml +curl https://raw.githubusercontent.com/tikv/pd/master/errors.toml -o errors/pd.toml +``` + +Then there are two tasks will be execute on the errors directory: +- Build a program that embed these errors, the program is used to quickly access information corresponding to error codes. +- Build the [error code document](https://pingcap.com/docs/stable/reference/error-codes/) from these errors. \ No newline at end of file From 76315c79c458b024b2c4fe8087486a2b9e8f92ce Mon Sep 17 00:00:00 2001 From: Han Fei Date: Wed, 20 May 2020 14:37:19 +0800 Subject: [PATCH 25/74] [storetikv] : Do not send batch commands to tiflash (#17282) Co-authored-by: pingcap-github-bot --- store/tikv/client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/store/tikv/client.go b/store/tikv/client.go index fbff332eb71b3..5f80aea85a29d 100644 --- a/store/tikv/client.go +++ b/store/tikv/client.go @@ -317,7 +317,8 @@ func (c *rpcClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.R c.recycleIdleConnArray() } - enableBatch := req.StoreTp != kv.TiDB + // TiDB will not send batch commands to TiFlash, to resolve the conflict with Batch Cop Request. + enableBatch := req.StoreTp != kv.TiDB && req.StoreTp != kv.TiFlash connArray, err := c.getConnArray(addr, enableBatch) if err != nil { return nil, errors.Trace(err) From f17da4a5d7d37c289f934a0c132b502cd7577bd8 Mon Sep 17 00:00:00 2001 From: Lingyu Song Date: Wed, 20 May 2020 15:54:44 +0800 Subject: [PATCH 26/74] plan, expression: support `is null` in hash partition pruning (#17281) --- expression/partition_pruner.go | 56 ++++++++++++------- expression/partition_pruner_test.go | 2 + expression/testdata/partition_pruner_in.json | 10 ++-- expression/testdata/partition_pruner_out.json | 37 ++++++++++++ 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/expression/partition_pruner.go b/expression/partition_pruner.go index bbf106a25cd6a..b3531b27d0508 100644 --- a/expression/partition_pruner.go +++ b/expression/partition_pruner.go @@ -79,7 +79,18 @@ func (p *hashPartitionPruner) reduceColumnEQ() bool { func (p *hashPartitionPruner) reduceConstantEQ() bool { for _, con := range p.conditions { - col, cond := validEqualCond(p.ctx, con) + var col *Column + var cond *Constant + if fn, ok := con.(*ScalarFunction); ok { + if fn.FuncName.L == ast.IsNull { + col, ok = fn.GetArgs()[0].(*Column) + if ok { + cond = NewNull() + } + } else { + col, cond = validEqualCond(p.ctx, con) + } + } if col != nil { id := p.getColID(col) if p.constantMap[id] != nil { @@ -94,18 +105,18 @@ func (p *hashPartitionPruner) reduceConstantEQ() bool { return false } -func (p *hashPartitionPruner) tryEvalPartitionExpr(piExpr Expression) (val int64, success bool, isNil bool) { +func (p *hashPartitionPruner) tryEvalPartitionExpr(piExpr Expression) (val int64, success bool, isNull bool) { switch pi := piExpr.(type) { case *ScalarFunction: if pi.FuncName.L == ast.Plus || pi.FuncName.L == ast.Minus || pi.FuncName.L == ast.Mul || pi.FuncName.L == ast.Div { left, right := pi.GetArgs()[0], pi.GetArgs()[1] - leftVal, ok, isNil := p.tryEvalPartitionExpr(left) - if !ok { - return 0, ok, isNil + leftVal, ok, isNull := p.tryEvalPartitionExpr(left) + if !ok || isNull { + return 0, ok, isNull } - rightVal, ok, isNil := p.tryEvalPartitionExpr(right) - if !ok { - return 0, ok, isNil + rightVal, ok, isNull := p.tryEvalPartitionExpr(right) + if !ok || isNull { + return 0, ok, isNull } switch pi.FuncName.L { case ast.Plus: @@ -123,11 +134,11 @@ func (p *hashPartitionPruner) tryEvalPartitionExpr(piExpr Expression) (val int64 val := p.constantMap[idx] if val != nil { pi.GetArgs()[0] = val - ret, _, err := pi.EvalInt(p.ctx, chunk.Row{}) + ret, isNull, err := pi.EvalInt(p.ctx, chunk.Row{}) if err != nil { return 0, false, false } - return ret, true, false + return ret, true, isNull } return 0, false, false } @@ -137,7 +148,7 @@ func (p *hashPartitionPruner) tryEvalPartitionExpr(piExpr Expression) (val int64 return 0, false, false } if val.IsNull() { - return 0, false, true + return 0, true, true } if val.Kind() == types.KindInt64 { return val.GetInt64(), true, false @@ -164,8 +175,8 @@ func newHashPartitionPruner() *hashPartitionPruner { } // solve eval the hash partition expression, the first return value represent the result of partition expression. The second -// return value is whether eval success. The third return value represent whether the eval result of partition value is null. -func (p *hashPartitionPruner) solve(ctx sessionctx.Context, conds []Expression, piExpr Expression) (val int64, ok bool, isNil bool) { +// return value is whether eval success. The third return value represent whether the query conditions is always false. +func (p *hashPartitionPruner) solve(ctx sessionctx.Context, conds []Expression, piExpr Expression) (val int64, ok bool, isAlwaysFalse bool) { p.ctx = ctx for _, cond := range conds { p.conditions = append(p.conditions, SplitCNFItems(cond)...) @@ -177,16 +188,19 @@ func (p *hashPartitionPruner) solve(ctx sessionctx.Context, conds []Expression, p.insertCol(col) } p.constantMap = make([]*Constant, p.numColumn) - conflict := p.reduceConstantEQ() - if conflict { - return 0, false, conflict + isAlwaysFalse = p.reduceConstantEQ() + if isAlwaysFalse { + return 0, false, isAlwaysFalse + } + isAlwaysFalse = p.reduceColumnEQ() + if isAlwaysFalse { + return 0, false, isAlwaysFalse } - conflict = p.reduceColumnEQ() - if conflict { - return 0, false, conflict + res, ok, isNull := p.tryEvalPartitionExpr(piExpr) + if isNull && ok { + return 0, ok, false } - res, ok, isNil := p.tryEvalPartitionExpr(piExpr) - return res, ok, isNil + return res, ok, false } // FastLocateHashPartition is used to get hash partition quickly. diff --git a/expression/partition_pruner_test.go b/expression/partition_pruner_test.go index 8f181d821169a..a132b6b84377b 100644 --- a/expression/partition_pruner_test.go +++ b/expression/partition_pruner_test.go @@ -69,6 +69,8 @@ func (s *testSuite2) TestHashPartitionPruner(c *C) { tk.MustExec("create table t3(id int, a int, b int, primary key(id, a)) partition by hash(id) partitions 10;") tk.MustExec("create table t4(d datetime, a int, b int, primary key(d, a)) partition by hash(year(d)) partitions 10;") tk.MustExec("create table t5(d date, a int, b int, primary key(d, a)) partition by hash(month(d)) partitions 10;") + tk.MustExec("create table t6(a int, b int) partition by hash(a) partitions 3;") + tk.MustExec("create table t7(a int, b int) partition by hash(a + b) partitions 10;") var input []string var output []struct { diff --git a/expression/testdata/partition_pruner_in.json b/expression/testdata/partition_pruner_in.json index 6f6e80fa85ff3..17edf2109826c 100644 --- a/expression/testdata/partition_pruner_in.json +++ b/expression/testdata/partition_pruner_in.json @@ -2,23 +2,23 @@ { "name": "TestHashPartitionPruner", "cases": [ - // Point Select. "explain select * from t1 where id = 7 and a = 6", "explain select * from t3 where id = 9 and a = 1", "explain select * from t2 where id = 9 and a = -110", "explain select * from t1 where id = -17", "explain select * from t2 where id = a and a = b and b = 2", - // Join. "explain select * from t1 join t2 on (t1.id = t2.id) where t1.id = 5 and t2.a = 7", "explain select * from t1 left join t2 on t1.id = 1 and t2.a = 2 where t2.id = 7", "explain select * from t2 join t1 on t1.id = t2.id and t2.a = t1.id and t2.id = 12", - // Negtive cases. "explain select * from t1 left join t2 on true where t1.a = 1 and false", "explain select * from t1 left join t2 on true where t1.a = 1 and null", "explain select * from t1 left join t2 on true where t1.a = null", - // Case with date. "explain select * from t4 where d = '2019-10-07 10:40:00' and a = 1", - "explain select * from t5 where d = '2019-10-07'" + "explain select * from t5 where d = '2019-10-07'", + "explain select * from t6 where a is null", + "explain select * from t6 where b is null", + "explain select * from t5 where d is null", + "explain select * from t7 where b = -3 and a is null" ] } ] diff --git a/expression/testdata/partition_pruner_out.json b/expression/testdata/partition_pruner_out.json index e3dcc64190589..9061476013d43 100644 --- a/expression/testdata/partition_pruner_out.json +++ b/expression/testdata/partition_pruner_out.json @@ -90,6 +90,43 @@ "├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t5, partition:p0, index:PRIMARY(d, a) range:[2019-10-07,2019-10-07], keep order:false, stats:pseudo", "└─TableRowIDScan_10(Probe) 10.00 cop[tikv] table:t5, partition:p0 keep order:false, stats:pseudo" ] + }, + { + "SQL": "explain select * from t6 where a is null", + "Result": [ + "TableReader_8 10.00 root data:Selection_7", + "└─Selection_7 10.00 cop[tikv] isnull(test_partition.t6.a)", + " └─TableFullScan_6 10000.00 cop[tikv] table:t6, partition:p0 keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain select * from t6 where b is null", + "Result": [ + "Union_9 30.00 root ", + "├─TableReader_12 10.00 root data:Selection_11", + "│ └─Selection_11 10.00 cop[tikv] isnull(test_partition.t6.b)", + "│ └─TableFullScan_10 10000.00 cop[tikv] table:t6, partition:p0 keep order:false, stats:pseudo", + "├─TableReader_15 10.00 root data:Selection_14", + "│ └─Selection_14 10.00 cop[tikv] isnull(test_partition.t6.b)", + "│ └─TableFullScan_13 10000.00 cop[tikv] table:t6, partition:p1 keep order:false, stats:pseudo", + "└─TableReader_18 10.00 root data:Selection_17", + " └─Selection_17 10.00 cop[tikv] isnull(test_partition.t6.b)", + " └─TableFullScan_16 10000.00 cop[tikv] table:t6, partition:p2 keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain select * from t5 where d is null", + "Result": [ + "TableDual_6 0.00 root rows:0" + ] + }, + { + "SQL": "explain select * from t7 where b = -3 and a is null", + "Result": [ + "TableReader_8 0.01 root data:Selection_7", + "└─Selection_7 0.01 cop[tikv] eq(test_partition.t7.b, -3), isnull(test_partition.t7.a)", + " └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p0 keep order:false, stats:pseudo" + ] } ] } From 5274e482048f61e7be69816c0aa348d3b8310607 Mon Sep 17 00:00:00 2001 From: HuaiyuXu <391585975@qq.com> Date: Wed, 20 May 2020 16:48:32 +0800 Subject: [PATCH 27/74] planner, executor: support insert/replace into partition (#17280) --- executor/executor_test.go | 90 ++++++++++++++++++++++++++++++++++++- go.mod | 6 +-- go.sum | 8 +++- planner/core/planbuilder.go | 19 +++++++- 4 files changed, 116 insertions(+), 7 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index b9839b9bcbd48..5f32fd15d18f6 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -5635,6 +5635,95 @@ func (s *testSuite1) TestInsertValuesWithSubQuery(c *C) { tk.MustQuery("select * from t2").Check(testkit.Rows("2 4 2", "3 5 5")) } +func (s *testSuite1) TestInsertIntoGivenPartitionSet(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1") + tk.MustExec(`create table t1( + a int(11) DEFAULT NULL, + b varchar(10) DEFAULT NULL, + UNIQUE KEY idx_a (a)) PARTITION BY RANGE (a) + (PARTITION p0 VALUES LESS THAN (10) ENGINE = InnoDB, + PARTITION p1 VALUES LESS THAN (20) ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN (30) ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN (40) ENGINE = InnoDB, + PARTITION p4 VALUES LESS THAN MAXVALUE ENGINE = InnoDB)`) + defer tk.MustExec("drop table if exists t1") + + // insert into + tk.MustExec("insert into t1 partition(p0) values(1, 'a'), (2, 'b')") + tk.MustQuery("select * from t1 partition(p0) order by a").Check(testkit.Rows("1 a", "2 b")) + tk.MustExec("insert into t1 partition(p0, p1) values(3, 'c'), (4, 'd')") + tk.MustQuery("select * from t1 partition(p1)").Check(testkit.Rows()) + + err := tk.ExecToErr("insert into t1 values(1, 'a')") + c.Assert(err.Error(), Equals, "[kv:1062]Duplicate entry '1' for key 'idx_a'") + + err = tk.ExecToErr("insert into t1 partition(p0, p_non_exist) values(1, 'a')") + c.Assert(err.Error(), Equals, "[table:1735]Unknown partition 'p_non_exist' in table 't1'") + + err = tk.ExecToErr("insert into t1 partition(p0, p1) values(40, 'a')") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + + // replace into + tk.MustExec("replace into t1 partition(p0) values(1, 'replace')") + tk.MustExec("replace into t1 partition(p0, p1) values(3, 'replace'), (4, 'replace')") + + err = tk.ExecToErr("replace into t1 values(1, 'a')") + tk.MustQuery("select * from t1 partition (p0) order by a").Check(testkit.Rows("1 a", "2 b", "3 replace", "4 replace")) + + err = tk.ExecToErr("replace into t1 partition(p0, p_non_exist) values(1, 'a')") + c.Assert(err.Error(), Equals, "[table:1735]Unknown partition 'p_non_exist' in table 't1'") + + err = tk.ExecToErr("replace into t1 partition(p0, p1) values(40, 'a')") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + + tk.MustExec("truncate table t1") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b char(10))") + defer tk.MustExec("drop table if exists t") + + // insert into general table + err = tk.ExecToErr("insert into t partition(p0, p1) values(1, 'a')") + c.Assert(err.Error(), Equals, "[planner:1747]PARTITION () clause on non partitioned table") + + // insert into from select + tk.MustExec("insert into t values(1, 'a'), (2, 'b')") + tk.MustExec("insert into t1 partition(p0) select * from t") + tk.MustQuery("select * from t1 partition(p0) order by a").Check(testkit.Rows("1 a", "2 b")) + + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(3, 'c'), (4, 'd')") + tk.MustExec("insert into t1 partition(p0, p1) select * from t") + tk.MustQuery("select * from t1 partition(p1) order by a").Check(testkit.Rows()) + tk.MustQuery("select * from t1 partition(p0) order by a").Check(testkit.Rows("1 a", "2 b", "3 c", "4 d")) + + err = tk.ExecToErr("insert into t1 select 1, 'a'") + c.Assert(err.Error(), Equals, "[kv:1062]Duplicate entry '1' for key 'idx_a'") + + err = tk.ExecToErr("insert into t1 partition(p0, p_non_exist) select 1, 'a'") + c.Assert(err.Error(), Equals, "[table:1735]Unknown partition 'p_non_exist' in table 't1'") + + err = tk.ExecToErr("insert into t1 partition(p0, p1) select 40, 'a'") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") + + // replace into from select + tk.MustExec("replace into t1 partition(p0) select 1, 'replace'") + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(3, 'replace'), (4, 'replace')") + tk.MustExec("replace into t1 partition(p0, p1) select * from t") + + err = tk.ExecToErr("replace into t1 values select 1, 'a'") + tk.MustQuery("select * from t1 partition (p0) order by a").Check(testkit.Rows("1 replace", "2 b", "3 replace", "4 replace")) + + err = tk.ExecToErr("replace into t1 partition(p0, p_non_exist) select 1, 'a'") + c.Assert(err.Error(), Equals, "[table:1735]Unknown partition 'p_non_exist' in table 't1'") + + err = tk.ExecToErr("replace into t1 partition(p0, p1) select 40, 'a'") + c.Assert(err.Error(), Equals, "[table:1748]Found a row not matching the given partition set") +} + func (s *testSuite1) TestUpdateGivenPartitionSet(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test;") @@ -5690,5 +5779,4 @@ func (s *testSuite1) TestUpdateGivenPartitionSet(c *C) { tk.MustExec("update t2 partition(p0) set a = 3 where a = 2") tk.MustExec("update t2 partition(p0, p3) set a = 33 where a = 1") - } diff --git a/go.mod b/go.mod index 10c9fbb57abaa..e97fbb4c5fbb9 100644 --- a/go.mod +++ b/go.mod @@ -30,8 +30,8 @@ require ( github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677 - github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd - github.com/pingcap/parser v0.0.0-20200515083134-baa47367bc23 + github.com/pingcap/log v0.0.0-20200511115504-543df19646ad + github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1 github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible @@ -49,7 +49,7 @@ require ( go.etcd.io/etcd v0.5.0-alpha.5.0.20191023171146-3cf2f69b5738 go.uber.org/atomic v1.6.0 go.uber.org/automaxprocs v1.2.0 - go.uber.org/zap v1.14.1 + go.uber.org/zap v1.15.0 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd diff --git a/go.sum b/go.sum index d8fd40ac6fe41..bb4dd00a30817 100644 --- a/go.sum +++ b/go.sum @@ -381,8 +381,10 @@ github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIf github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc= github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/parser v0.0.0-20200424075042-8222d8b724a4/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= -github.com/pingcap/parser v0.0.0-20200515083134-baa47367bc23 h1:Ljs6GisUJJTA95ui5HQ68wgpLcCx50CJcfd5TT+Ead0= -github.com/pingcap/parser v0.0.0-20200515083134-baa47367bc23/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= +github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657 h1:2ceTso30kmgMeddZ4iZ6zrK8N9eFF8zmCa1hSSE1tXc= +github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= +github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1 h1:xPCqyGxvHfAZrBnfr4Dbm7RZFCL2QIKz66UyJYYbrOM= +github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 h1:JTzYYukREvxVSKW/ncrzNjFitd8snoQ/Xz32pw8i+s8= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2/go.mod h1:s+utZtXDznOiL24VK0qGmtoHjjXNsscJx3m1n8cC56s= github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= @@ -541,6 +543,8 @@ go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 7c8910f835bc3..ae6fb6bb18628 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -42,7 +42,7 @@ import ( "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" + "github.com/pingcap/tidb/types/parser_driver" util2 "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/hint" @@ -52,6 +52,7 @@ import ( "github.com/pingcap/tidb/util/set" "github.com/cznic/mathutil" + "github.com/pingcap/tidb/table/tables" "go.uber.org/zap" ) @@ -1991,6 +1992,22 @@ func (b *PlanBuilder) buildInsert(ctx context.Context, insert *ast.InsertStmt) ( IsReplace: insert.IsReplace, }.Init(b.ctx) + if tableInfo.GetPartitionInfo() != nil && len(insert.PartitionNames) != 0 { + givenPartitionSets := make(map[int64]struct{}, len(insert.PartitionNames)) + // check partition by name. + for _, name := range insert.PartitionNames { + id, err := tables.FindPartitionByName(tableInfo, name.L) + if err != nil { + return nil, err + } + givenPartitionSets[id] = struct{}{} + } + pt := tableInPlan.(table.PartitionedTable) + insertPlan.Table = tables.NewPartitionTableithGivenSets(pt, givenPartitionSets) + } else if len(insert.PartitionNames) != 0 { + return nil, ErrPartitionClauseOnNonpartitioned + } + var authErr error if b.ctx.GetSessionVars().User != nil { authErr = ErrTableaccessDenied.GenWithStackByArgs("INSERT", b.ctx.GetSessionVars().User.Hostname, From 313ed01f036b6498b7972892ce56487e79d961df Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Wed, 20 May 2020 17:05:14 +0800 Subject: [PATCH 28/74] expression,executor: fix DIV() as partition expression behavior under ERROR_FOR_DIVISION_BY_ZERO sql_mode (#17302) --- executor/executor_test.go | 13 +++++++++++++ expression/builtin_arithmetic.go | 10 +++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index 5f32fd15d18f6..d37473cae3b2b 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -5635,6 +5635,19 @@ func (s *testSuite1) TestInsertValuesWithSubQuery(c *C) { tk.MustQuery("select * from t2").Check(testkit.Rows("2 4 2", "3 5 5")) } +func (s *testSuite1) TestDIVZeroInPartitionExpr(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int) partition by range (10 div a) (partition p0 values less than (10), partition p1 values less than maxvalue)") + defer tk.MustExec("drop table if exists t1") + + tk.MustExec("set @@sql_mode=''") + tk.MustExec("insert into t1 values (NULL), (0), (1)") + tk.MustExec("set @@sql_mode='STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO'") + tk.MustGetErrCode("insert into t1 values (NULL), (0), (1)", mysql.ErrDivisionByZero) +} + func (s *testSuite1) TestInsertIntoGivenPartitionSet(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test;") diff --git a/expression/builtin_arithmetic.go b/expression/builtin_arithmetic.go index 7bd59ec95347a..e2e7b839c9784 100644 --- a/expression/builtin_arithmetic.go +++ b/expression/builtin_arithmetic.go @@ -747,16 +747,20 @@ func (s *builtinArithmeticIntDivideDecimalSig) Clone() builtinFunc { } func (s *builtinArithmeticIntDivideIntSig) evalInt(row chunk.Row) (int64, bool, error) { - b, isNull, err := s.args[1].EvalInt(s.ctx, row) + return s.evalIntWithCtx(s.ctx, row) +} + +func (s *builtinArithmeticIntDivideIntSig) evalIntWithCtx(sctx sessionctx.Context, row chunk.Row) (int64, bool, error) { + b, isNull, err := s.args[1].EvalInt(sctx, row) if isNull || err != nil { return 0, isNull, err } if b == 0 { - return 0, true, handleDivisionByZeroError(s.ctx) + return 0, true, handleDivisionByZeroError(sctx) } - a, isNull, err := s.args[0].EvalInt(s.ctx, row) + a, isNull, err := s.args[0].EvalInt(sctx, row) if isNull || err != nil { return 0, isNull, err } From 4078eb4ae5ae2036ff7f60a7585cbc3c5601e448 Mon Sep 17 00:00:00 2001 From: crazycs Date: Wed, 20 May 2020 17:10:44 +0800 Subject: [PATCH 29/74] util/admin: support admin recover index on the partition table (#17195) --- executor/admin.go | 39 ++++++++++++++++------- executor/admin_test.go | 70 ++++++++++++++++++++++++++++++++++++++++++ executor/builder.go | 12 ++------ table/tables/tables.go | 11 +++++++ 4 files changed, 112 insertions(+), 20 deletions(-) diff --git a/executor/admin.go b/executor/admin.go index ad5f602700e88..d50593042dc9e 100644 --- a/executor/admin.go +++ b/executor/admin.go @@ -173,9 +173,10 @@ type RecoverIndexExec struct { done bool - index table.Index - table table.Table - batchSize int + index table.Index + table table.Table + physicalID int64 + batchSize int columns []*model.ColumnInfo colFieldTypes []*types.FieldType @@ -216,7 +217,7 @@ func (e *RecoverIndexExec) Open(ctx context.Context) error { func (e *RecoverIndexExec) constructTableScanPB(pbColumnInfos []*tipb.ColumnInfo) *tipb.Executor { tblScan := &tipb.TableScan{ - TableId: e.table.Meta().ID, + TableId: e.physicalID, Columns: pbColumnInfos, } @@ -254,15 +255,14 @@ func (e *RecoverIndexExec) buildDAGPB(txn kv.Transaction, limitCnt uint64) (*tip return dagReq, nil } -func (e *RecoverIndexExec) buildTableScan(ctx context.Context, txn kv.Transaction, t table.Table, startHandle kv.Handle, limitCnt uint64) (distsql.SelectResult, error) { +func (e *RecoverIndexExec) buildTableScan(ctx context.Context, txn kv.Transaction, startHandle kv.Handle, limitCnt uint64) (distsql.SelectResult, error) { dagPB, err := e.buildDAGPB(txn, limitCnt) if err != nil { return nil, err } - tblInfo := e.table.Meta() ranges := []*ranger.Range{{LowVal: []types.Datum{types.NewIntDatum(startHandle.IntValue())}, HighVal: []types.Datum{types.NewIntDatum(math.MaxInt64)}}} var builder distsql.RequestBuilder - kvReq, err := builder.SetTableRanges(tblInfo.ID, ranges, nil). + kvReq, err := builder.SetTableRanges(e.physicalID, ranges, nil). SetDAGRequest(dagPB). SetStartTS(txn.StartTS()). SetKeepOrder(true). @@ -409,7 +409,7 @@ func (e *RecoverIndexExec) batchMarkDup(txn kv.Transaction, rows []recoverRows) func (e *RecoverIndexExec) backfillIndexInTxn(ctx context.Context, txn kv.Transaction, startHandle kv.Handle) (result backfillResult, err error) { result.nextHandle = startHandle - srcResult, err := e.buildTableScan(ctx, txn, e.table, startHandle, uint64(e.batchSize)) + srcResult, err := e.buildTableScan(ctx, txn, startHandle, uint64(e.batchSize)) if err != nil { return result, err } @@ -454,9 +454,26 @@ func (e *RecoverIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { return nil } - totalAddedCnt, totalScanCnt, err := e.backfillIndex(ctx) - if err != nil { - return err + var totalAddedCnt, totalScanCnt int64 + var err error + if tbl, ok := e.table.(table.PartitionedTable); ok { + pi := e.table.Meta().GetPartitionInfo() + for _, p := range pi.Definitions { + e.table = tbl.GetPartition(p.ID) + e.index = tables.GetWritableIndexByName(e.index.Meta().Name.L, e.table) + e.physicalID = p.ID + addedCnt, scanCnt, err := e.backfillIndex(ctx) + totalAddedCnt += addedCnt + totalScanCnt += scanCnt + if err != nil { + return err + } + } + } else { + totalAddedCnt, totalScanCnt, err = e.backfillIndex(ctx) + if err != nil { + return err + } } req.AppendInt64(0, totalAddedCnt) diff --git a/executor/admin_test.go b/executor/admin_test.go index c7229218e5eaf..89a726d737884 100644 --- a/executor/admin_test.go +++ b/executor/admin_test.go @@ -23,6 +23,7 @@ import ( "github.com/pingcap/parser/model" "github.com/pingcap/tidb/executor" "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/mock" @@ -160,6 +161,75 @@ func (s *testSuite5) TestAdminRecoverIndex(c *C) { tk.MustExec("admin check table admin_test") } +func (s *testSuite5) TestAdminRecoverPartitionTableIndex(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + getTable := func() table.Table { + s.ctx = mock.NewContext() + s.ctx.Store = s.store + is := s.domain.InfoSchema() + dbName := model.NewCIStr("test") + tblName := model.NewCIStr("admin_test") + tbl, err := is.TableByName(dbName, tblName) + c.Assert(err, IsNil) + return tbl + } + + checkFunc := func(tbl table.Table, pid int64, idxValue int) { + idxInfo := tbl.Meta().FindIndexByName("c2") + indexOpr := tables.NewIndex(pid, tbl.Meta(), idxInfo) + sc := s.ctx.GetSessionVars().StmtCtx + txn, err := s.store.Begin() + c.Assert(err, IsNil) + err = indexOpr.Delete(sc, txn, types.MakeDatums(idxValue), kv.IntHandle(idxValue)) + c.Assert(err, IsNil) + err = txn.Commit(context.Background()) + c.Assert(err, IsNil) + err = tk.ExecToErr("admin check table admin_test") + c.Assert(err, NotNil) + c.Assert(executor.ErrAdminCheckTable.Equal(err), IsTrue) + + r := tk.MustQuery("SELECT COUNT(*) FROM admin_test USE INDEX(c2)") + r.Check(testkit.Rows("2")) + + r = tk.MustQuery("admin recover index admin_test c2") + r.Check(testkit.Rows("1 3")) + + r = tk.MustQuery("SELECT COUNT(*) FROM admin_test USE INDEX(c2)") + r.Check(testkit.Rows("3")) + tk.MustExec("admin check table admin_test") + } + + // Test for hash partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, primary key (c1), index (c2)) partition by hash(c1) partitions 3;") + tk.MustExec("insert admin_test (c1, c2) values (0, 0), (1, 1), (2, 2)") + r := tk.MustQuery("admin recover index admin_test c2") + r.Check(testkit.Rows("0 3")) + tbl := getTable() + pi := tbl.Meta().GetPartitionInfo() + c.Assert(pi, NotNil) + for i, p := range pi.Definitions { + checkFunc(tbl, p.ID, i) + } + + // Test for range partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec(`create table admin_test (c1 int, c2 int, c3 int default 1, primary key (c1), index (c2)) PARTITION BY RANGE ( c1 ) ( + PARTITION p0 VALUES LESS THAN (5), + PARTITION p1 VALUES LESS THAN (10), + PARTITION p2 VALUES LESS THAN (MAXVALUE))`) + tk.MustExec("insert admin_test (c1, c2) values (0, 0), (6, 6), (12, 12)") + r = tk.MustQuery("admin recover index admin_test c2") + r.Check(testkit.Rows("0 3")) + tbl = getTable() + pi = tbl.Meta().GetPartitionInfo() + c.Assert(pi, NotNil) + for i, p := range pi.Definitions { + checkFunc(tbl, p.ID, i*6) + } +} + func (s *testSuite5) TestAdminRecoverIndex1(c *C) { tk := testkit.NewTestKit(c, s.store) s.ctx = mock.NewContext() diff --git a/executor/builder.go b/executor/builder.go index 2479aa6777f95..438f868b2d13c 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -44,6 +44,7 @@ import ( "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/statistics" "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/admin" @@ -432,15 +433,7 @@ func (b *executorBuilder) buildRecoverIndex(v *plannercore.RecoverIndex) Executo return nil } idxName := strings.ToLower(v.IndexName) - indices := t.WritableIndices() - var index table.Index - for _, idx := range indices { - if idxName == idx.Meta().Name.L { - index = idx - break - } - } - + index := tables.GetWritableIndexByName(idxName, t) if index == nil { b.err = errors.Errorf("index `%v` is not found in table `%v`.", v.IndexName, v.Table.Name.O) return nil @@ -450,6 +443,7 @@ func (b *executorBuilder) buildRecoverIndex(v *plannercore.RecoverIndex) Executo columns: buildRecoverIndexCols(tblInfo, index.Meta()), index: index, table: t, + physicalID: t.Meta().ID, } return e } diff --git a/table/tables/tables.go b/table/tables/tables.go index ca9cbfe8915e9..0c06ee329d971 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -204,6 +204,17 @@ func (t *TableCommon) WritableIndices() []table.Index { return writable } +// GetWritableIndexByName gets the index meta from the table by the index name. +func GetWritableIndexByName(idxName string, t table.Table) table.Index { + indices := t.WritableIndices() + for _, idx := range indices { + if idxName == idx.Meta().Name.L { + return idx + } + } + return nil +} + // DeletableIndices implements table.Table DeletableIndices interface. func (t *TableCommon) DeletableIndices() []table.Index { // All indices are deletable because we don't need to check StateNone. From 3afb631ef63db68f05d605b0f76456ffce37dc79 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Wed, 20 May 2020 19:27:14 +0800 Subject: [PATCH 30/74] planner/core: support range partition pruning for 'in' expression (#17210) --- cmd/explaintest/r/partition_pruning.result | 5 ++ cmd/explaintest/t/partition_pruning.test | 2 +- planner/core/integration_test.go | 23 ++++++ planner/core/rule_partition_processor.go | 45 ++++++++++ .../core/testdata/integration_suite_in.json | 12 +++ .../core/testdata/integration_suite_out.json | 82 +++++++++++++++++++ 6 files changed, 168 insertions(+), 1 deletion(-) diff --git a/cmd/explaintest/r/partition_pruning.result b/cmd/explaintest/r/partition_pruning.result index da4222bad3a22..a91fa7c3b7405 100644 --- a/cmd/explaintest/r/partition_pruning.result +++ b/cmd/explaintest/r/partition_pruning.result @@ -3595,6 +3595,11 @@ id estRows task access object operator info TableReader_8 10.00 root data:Selection_7 └─Selection_7 10.00 cop[tikv] eq(test.t2.a, 833) └─TableFullScan_6 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo +explain select * from t2 where a in (10,20,30); +id estRows task access object operator info +TableReader_8 30.00 root data:Selection_7 +└─Selection_7 30.00 cop[tikv] in(test.t2.a, 10, 20, 30) + └─TableFullScan_6 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo explain select * from t2 where (a = 100 OR a = 900); id estRows task access object operator info Union_8 40.00 root diff --git a/cmd/explaintest/t/partition_pruning.test b/cmd/explaintest/t/partition_pruning.test index e7741b3196a0c..704172c8aba80 100644 --- a/cmd/explaintest/t/partition_pruning.test +++ b/cmd/explaintest/t/partition_pruning.test @@ -766,6 +766,7 @@ explain select * from t2; explain select * from t2 where a = 101; explain select * from t2 where a = 550; explain select * from t2 where a = 833; +explain select * from t2 where a in (10,20,30); explain select * from t2 where (a = 100 OR a = 900); explain select * from t2 where (a > 100 AND a < 600); explain select * from t2 where b = 4; @@ -1010,4 +1011,3 @@ explain select * from t where ts <= '2020-04-14 23:59:59.123' -- p0,p1; explain select * from t where ts <= '2020-04-25 00:00:00' -- p0,p1,p2; explain select * from t where ts > '2020-04-25 00:00:00' or ts < '2020-01-02 00:00:00' -- p0; explain select * from t where ts > '2020-04-02 00:00:00' and ts < '2020-04-07 00:00:00' -- p0,p1; - diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index ff741bd195510..0349a2aabef7f 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -557,6 +557,29 @@ func (s *testIntegrationSuite) TestPartitionTableStats(c *C) { } } +func (s *testIntegrationSuite) TestPartitionPruningForInExpr(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(11), b int) partition by range (a) (partition p0 values less than (4), partition p1 values less than(10), partition p2 values less than maxvalue);") + tk.MustExec("insert into t values (1, 1),(10, 10),(11, 11)") + + 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(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + func (s *testIntegrationSuite) TestErrNoDB(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("create user test") diff --git a/planner/core/rule_partition_processor.go b/planner/core/rule_partition_processor.go index d96a668bb0fae..f2c01d54274c3 100644 --- a/planner/core/rule_partition_processor.go +++ b/planner/core/rule_partition_processor.go @@ -423,6 +423,12 @@ func partitionRangeForExpr(sctx sessionctx.Context, expr expression.Expression, args := op.GetArgs() newRange := partitionRangeForOrExpr(sctx, args[0], args[1], pruner) return result.intersection(newRange) + } else if op.FuncName.L == ast.In { + if p, ok := pruner.(*rangePruner); ok { + newRange := partitionRangeForInExpr(sctx, op.GetArgs(), p) + return result.intersection(newRange) + } + return result } } @@ -450,6 +456,12 @@ type rangePruner struct { } func (p *rangePruner) partitionRangeForExpr(sctx sessionctx.Context, expr expression.Expression) (int, int, bool) { + if constExpr, ok := expr.(*expression.Constant); ok { + if b, err := constExpr.Value.ToBool(sctx.GetSessionVars().StmtCtx); err == nil && b == 0 { + // A constant false expression. + return 0, 0, true + } + } dataForPrune, ok := p.extractDataForPrune(sctx, expr) if !ok { return 0, 0, false @@ -472,6 +484,39 @@ func partitionRangeForOrExpr(sctx sessionctx.Context, expr1, expr2 expression.Ex return tmp1.union(tmp2) } +func partitionRangeForInExpr(sctx sessionctx.Context, args []expression.Expression, + pruner *rangePruner) partitionRangeOR { + col, ok := args[0].(*expression.Column) + if !ok || col.ID != pruner.col.ID { + return pruner.fullRange() + } + + var result partitionRangeOR + unsigned := mysql.HasUnsignedFlag(col.RetType.Flag) + for i := 1; i < len(args); i++ { + constExpr, ok := args[i].(*expression.Constant) + if !ok { + return pruner.fullRange() + } + switch constExpr.Value.Kind() { + case types.KindInt64, types.KindUint64: + case types.KindNull: + result = append(result, partitionRange{0, 1}) + continue + default: + return pruner.fullRange() + } + val, err := constExpr.Value.ToInt64(sctx.GetSessionVars().StmtCtx) + if err != nil { + return pruner.fullRange() + } + + start, end := pruneUseBinarySearch(pruner.lessThan, dataForPrune{op: ast.EQ, c: val}, unsigned) + result = append(result, partitionRange{start, end}) + } + return result.simplify() +} + // monotoneIncFuncs are those functions that for any x y, if x > y => f(x) > f(y) var monotoneIncFuncs = map[string]struct{}{ ast.ToDays: {}, diff --git a/planner/core/testdata/integration_suite_in.json b/planner/core/testdata/integration_suite_in.json index 18c95deffdc73..360b6bb3d9ad2 100644 --- a/planner/core/testdata/integration_suite_in.json +++ b/planner/core/testdata/integration_suite_in.json @@ -115,5 +115,17 @@ "select /*+ IGNORE_INDEX() */ * from t1, t2 where t1.a=t2.a", "select /*+ USE_INDEX_MERGE() */ * from t1, t2 where t1.a=t2.a" ] + }, + { + "name": "TestPartitionPruningForInExpr", + "cases": [ + "explain select * from t where a in (1, 2,'11')", + "explain select * from t where a in (17, null)", + "explain select * from t where a in (16, 'abc')", + "explain select * from t where a in (15, 0.12, 3.47)", + "explain select * from t where a in (0.12, 3.47)", + "explain select * from t where a in (14, floor(3.47))", + "explain select * from t where b in (3, 4)" + ] } ] diff --git a/planner/core/testdata/integration_suite_out.json b/planner/core/testdata/integration_suite_out.json index f497926424c85..7418675766b40 100644 --- a/planner/core/testdata/integration_suite_out.json +++ b/planner/core/testdata/integration_suite_out.json @@ -500,5 +500,87 @@ ] } ] + }, + { + "Name": "TestPartitionPruningForInExpr", + "Cases": [ + { + "SQL": "explain select * from t where a in (1, 2,'11')", + "Plan": [ + "Union_8 60.00 root ", + "├─TableReader_11 30.00 root data:Selection_10", + "│ └─Selection_10 30.00 cop[tikv] in(test.t.a, 1, 2, 11)", + "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", + "└─TableReader_14 30.00 root data:Selection_13", + " └─Selection_13 30.00 cop[tikv] in(test.t.a, 1, 2, 11)", + " └─TableFullScan_12 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain select * from t where a in (17, null)", + "Plan": [ + "Union_8 20.00 root ", + "├─TableReader_11 10.00 root data:Selection_10", + "│ └─Selection_10 10.00 cop[tikv] in(test.t.a, 17, NULL)", + "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", + "└─TableReader_14 10.00 root data:Selection_13", + " └─Selection_13 10.00 cop[tikv] in(test.t.a, 17, NULL)", + " └─TableFullScan_12 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain select * from t where a in (16, 'abc')", + "Plan": [ + "Union_8 40.00 root ", + "├─TableReader_11 20.00 root data:Selection_10", + "│ └─Selection_10 20.00 cop[tikv] in(test.t.a, 16, 0)", + "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", + "└─TableReader_14 20.00 root data:Selection_13", + " └─Selection_13 20.00 cop[tikv] in(test.t.a, 16, 0)", + " └─TableFullScan_12 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain select * from t where a in (15, 0.12, 3.47)", + "Plan": [ + "TableReader_8 10.00 root data:Selection_7", + "└─Selection_7 10.00 cop[tikv] or(eq(test.t.a, 15), 0)", + " └─TableFullScan_6 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain select * from t where a in (0.12, 3.47)", + "Plan": [ + "TableDual_6 0.00 root rows:0" + ] + }, + { + "SQL": "explain select * from t where a in (14, floor(3.47))", + "Plan": [ + "Union_8 40.00 root ", + "├─TableReader_11 20.00 root data:Selection_10", + "│ └─Selection_10 20.00 cop[tikv] in(test.t.a, 14, 3)", + "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", + "└─TableReader_14 20.00 root data:Selection_13", + " └─Selection_13 20.00 cop[tikv] in(test.t.a, 14, 3)", + " └─TableFullScan_12 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo" + ] + }, + { + "SQL": "explain select * from t where b in (3, 4)", + "Plan": [ + "Union_9 60.00 root ", + "├─TableReader_12 20.00 root data:Selection_11", + "│ └─Selection_11 20.00 cop[tikv] in(test.t.b, 3, 4)", + "│ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", + "├─TableReader_15 20.00 root data:Selection_14", + "│ └─Selection_14 20.00 cop[tikv] in(test.t.b, 3, 4)", + "│ └─TableFullScan_13 10000.00 cop[tikv] table:t, partition:p1 keep order:false, stats:pseudo", + "└─TableReader_18 20.00 root data:Selection_17", + " └─Selection_17 20.00 cop[tikv] in(test.t.b, 3, 4)", + " └─TableFullScan_16 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo" + ] + } + ] } ] From 0d33a845766733fbb9e40dae8b7bf38fb0d3498b Mon Sep 17 00:00:00 2001 From: "Zhuomin(Charming) Liu" Date: Wed, 20 May 2020 19:54:37 +0800 Subject: [PATCH 31/74] session: fix unsecessfully set the isolation read engines (#17258) --- sessionctx/variable/session.go | 12 +++++++++++- sessionctx/variable/session_test.go | 19 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 21eb98d7cd115..cb58fd1f79bbe 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -682,7 +682,7 @@ func NewSessionVars() *SessionVars { AllowRemoveAutoInc: DefTiDBAllowRemoveAutoInc, UsePlanBaselines: DefTiDBUsePlanBaselines, EvolvePlanBaselines: DefTiDBEvolvePlanBaselines, - IsolationReadEngines: map[kv.StoreType]struct{}{kv.TiKV: {}, kv.TiFlash: {}, kv.TiDB: {}}, + IsolationReadEngines: make(map[kv.StoreType]struct{}), LockWaitTimeout: DefInnodbLockWaitTimeout * 1000, MetricSchemaStep: DefTiDBMetricSchemaStep, MetricSchemaRangeDuration: DefTiDBMetricSchemaRangeDuration, @@ -741,6 +741,16 @@ func NewSessionVars() *SessionVars { enableChunkRPC = "0" } terror.Log(vars.SetSystemVar(TiDBEnableChunkRPC, enableChunkRPC)) + for _, engine := range config.GetGlobalConfig().IsolationRead.Engines { + switch engine { + case kv.TiFlash.Name(): + vars.IsolationReadEngines[kv.TiFlash] = struct{}{} + case kv.TiKV.Name(): + vars.IsolationReadEngines[kv.TiKV] = struct{}{} + case kv.TiDB.Name(): + vars.IsolationReadEngines[kv.TiDB] = struct{}{} + } + } return vars } diff --git a/sessionctx/variable/session_test.go b/sessionctx/variable/session_test.go index 198dbdd1de0f3..156881b611ce9 100644 --- a/sessionctx/variable/session_test.go +++ b/sessionctx/variable/session_test.go @@ -19,6 +19,8 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/parser" "github.com/pingcap/parser/auth" + "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/types" @@ -26,7 +28,7 @@ import ( "github.com/pingcap/tidb/util/mock" ) -var _ = Suite(&testSessionSuite{}) +var _ = SerialSuites(&testSessionSuite{}) type testSessionSuite struct { } @@ -216,3 +218,18 @@ select * from t;` }) c.Assert(logString, Equals, resultString) } + +func (*testSessionSuite) TestIsolationRead(c *C) { + originIsolationEngines := config.GetGlobalConfig().IsolationRead.Engines + defer func() { + config.GetGlobalConfig().IsolationRead.Engines = originIsolationEngines + }() + config.GetGlobalConfig().IsolationRead.Engines = []string{"tiflash", "tidb"} + sessVars := variable.NewSessionVars() + _, ok := sessVars.IsolationReadEngines[kv.TiDB] + c.Assert(ok, Equals, true) + _, ok = sessVars.IsolationReadEngines[kv.TiKV] + c.Assert(ok, Equals, false) + _, ok = sessVars.IsolationReadEngines[kv.TiFlash] + c.Assert(ok, Equals, true) +} From b248783dfe5ba363e25a19c28c5a1930e17af201 Mon Sep 17 00:00:00 2001 From: "Zhuomin(Charming) Liu" Date: Wed, 20 May 2020 21:50:15 +0800 Subject: [PATCH 32/74] planner: fix wrong agg function when agg push down union (#17022) --- cmd/explaintest/r/explain_easy.result | 18 +++++------ executor/aggregate_test.go | 32 +++++++++++++++++++ planner/core/rule_aggregation_push_down.go | 17 +++++++--- planner/core/testdata/plan_suite_out.json | 24 +++++++------- .../testdata/plan_suite_unexported_out.json | 4 +-- 5 files changed, 68 insertions(+), 27 deletions(-) diff --git a/cmd/explaintest/r/explain_easy.result b/cmd/explaintest/r/explain_easy.result index a7ec17fafaba2..81c76205b69ca 100644 --- a/cmd/explaintest/r/explain_easy.result +++ b/cmd/explaintest/r/explain_easy.result @@ -164,10 +164,10 @@ id estRows task access object operator info Union_17 26000.00 root ├─HashAgg_21 16000.00 root group by:Column#10, funcs:firstrow(Column#12)->Column#10 │ └─Union_22 16000.00 root -│ ├─StreamAgg_27 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#12 +│ ├─StreamAgg_27 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#12, funcs:firstrow(test.t2.c1)->Column#10 │ │ └─IndexReader_40 10000.00 root index:IndexFullScan_39 │ │ └─IndexFullScan_39 10000.00 cop[tikv] table:t2, index:c1(c1) keep order:true, stats:pseudo -│ └─StreamAgg_45 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#12 +│ └─StreamAgg_45 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#12, funcs:firstrow(test.t2.c1)->Column#10 │ └─IndexReader_58 10000.00 root index:IndexFullScan_57 │ └─IndexFullScan_57 10000.00 cop[tikv] table:t2, index:c1(c1) keep order:true, stats:pseudo └─IndexReader_63 10000.00 root index:IndexFullScan_62 @@ -176,13 +176,13 @@ explain select c1 from t2 union all select c1 from t2 union select c1 from t2; id estRows task access object operator info HashAgg_18 24000.00 root group by:Column#10, funcs:firstrow(Column#11)->Column#10 └─Union_19 24000.00 root - ├─StreamAgg_24 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#11 + ├─StreamAgg_24 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#11, funcs:firstrow(test.t2.c1)->Column#10 │ └─IndexReader_37 10000.00 root index:IndexFullScan_36 │ └─IndexFullScan_36 10000.00 cop[tikv] table:t2, index:c1(c1) keep order:true, stats:pseudo - ├─StreamAgg_42 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#11 + ├─StreamAgg_42 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#11, funcs:firstrow(test.t2.c1)->Column#10 │ └─IndexReader_55 10000.00 root index:IndexFullScan_54 │ └─IndexFullScan_54 10000.00 cop[tikv] table:t2, index:c1(c1) keep order:true, stats:pseudo - └─StreamAgg_60 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#11 + └─StreamAgg_60 8000.00 root group by:test.t2.c1, funcs:firstrow(test.t2.c1)->Column#11, funcs:firstrow(test.t2.c1)->Column#10 └─IndexReader_73 10000.00 root index:IndexFullScan_72 └─IndexFullScan_72 10000.00 cop[tikv] table:t2, index:c1(c1) keep order:true, stats:pseudo select * from information_schema.tidb_indexes where table_name='t4'; @@ -669,17 +669,17 @@ id estRows task access object operator info Sort_13 2.00 root Column#3 └─HashAgg_17 2.00 root group by:Column#3, funcs:firstrow(Column#6)->Column#3 └─Union_18 2.00 root - ├─HashAgg_19 1.00 root group by:1, funcs:firstrow(0)->Column#6 + ├─HashAgg_19 1.00 root group by:1, funcs:firstrow(0)->Column#6, funcs:firstrow(0)->Column#3 │ └─TableDual_22 1.00 root rows:1 - └─HashAgg_25 1.00 root group by:1, funcs:firstrow(1)->Column#6 + └─HashAgg_25 1.00 root group by:1, funcs:firstrow(1)->Column#6, funcs:firstrow(1)->Column#3 └─TableDual_28 1.00 root rows:1 explain SELECT 0 AS a FROM dual UNION (SELECT 1 AS a FROM dual ORDER BY a); id estRows task access object operator info HashAgg_15 2.00 root group by:Column#3, funcs:firstrow(Column#6)->Column#3 └─Union_16 2.00 root - ├─HashAgg_17 1.00 root group by:1, funcs:firstrow(0)->Column#6 + ├─HashAgg_17 1.00 root group by:1, funcs:firstrow(0)->Column#6, funcs:firstrow(0)->Column#3 │ └─TableDual_20 1.00 root rows:1 - └─StreamAgg_27 1.00 root group by:Column#1, funcs:firstrow(Column#1)->Column#6 + └─StreamAgg_27 1.00 root group by:Column#1, funcs:firstrow(Column#1)->Column#6, funcs:firstrow(Column#1)->Column#3 └─Projection_32 1.00 root 1->Column#1 └─TableDual_33 1.00 root rows:1 create table t (i int key, j int, unique key (i, j)); diff --git a/executor/aggregate_test.go b/executor/aggregate_test.go index 2364ddcd07ff9..fee57a7239037 100644 --- a/executor/aggregate_test.go +++ b/executor/aggregate_test.go @@ -729,6 +729,38 @@ func (s *testSuiteAgg) TestIssue16279(c *C) { tk.MustQuery("select count(a) , date_format(a, '%Y-%m-%d') as xx from s group by xx") } +func (s *testSuiteAgg) TestAggPushDownPartitionTable(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec(`CREATE TABLE t1 ( + a int(11) DEFAULT NULL, + b tinyint(4) NOT NULL, + PRIMARY KEY (b) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin + PARTITION BY RANGE ( b ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20), + PARTITION p2 VALUES LESS THAN (30), + PARTITION p3 VALUES LESS THAN (40), + PARTITION p4 VALUES LESS THAN (MAXVALUE) + )`) + tk.MustExec("insert into t1 values (0, 0), (1, 1), (1, 2), (1, 3), (2, 4), (2, 5), (2, 6), (3, 7), (3, 10), (3, 11), (12, 12), (12, 13), (14, 14), (14, 15), (20, 20), (20, 21), (20, 22), (23, 23), (23, 24), (23, 25), (31, 30), (31, 31), (31, 32), (33, 33), (33, 34), (33, 35), (36, 36), (80, 80), (90, 90), (100, 100)") + tk.MustExec("set @@tidb_opt_agg_push_down = 1") + tk.MustQuery("select /*+ AGG_TO_COP() */ sum(a), sum(b) from t1 where a < 40 group by a").Sort().Check(testkit.Rows( + "0 0", + "24 25", + "28 29", + "3 6", + "36 36", + "6 15", + "60 63", + "69 72", + "9 28", + "93 93", + "99 102")) +} + func (s *testSuiteAgg) TestIssue13652(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/planner/core/rule_aggregation_push_down.go b/planner/core/rule_aggregation_push_down.go index 179a2d311f414..dc44480da4362 100644 --- a/planner/core/rule_aggregation_push_down.go +++ b/planner/core/rule_aggregation_push_down.go @@ -320,7 +320,7 @@ func (a *aggregationPushDownSolver) splitPartialAgg(agg *LogicalAggregation) (pu // pushAggCrossUnion will try to push the agg down to the union. If the new aggregation's group-by columns doesn't contain unique key. // We will return the new aggregation. Otherwise we will transform the aggregation to projection. -func (a *aggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, unionSchema *expression.Schema, unionChild LogicalPlan) LogicalPlan { +func (a *aggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, unionSchema *expression.Schema, unionChild LogicalPlan) (LogicalPlan, error) { ctx := agg.ctx newAgg := LogicalAggregation{ AggFuncs: make([]*aggregation.AggFuncDesc, 0, len(agg.AggFuncs)), @@ -340,6 +340,12 @@ func (a *aggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, u for _, gbyExpr := range agg.GroupByItems { newExpr := expression.ColumnSubstitute(gbyExpr, unionSchema, expression.Column2Exprs(unionChild.Schema().Columns)) newAgg.GroupByItems = append(newAgg.GroupByItems, newExpr) + // TODO: if there is a duplicated first_row function, we can delete it. + firstRow, err := aggregation.NewAggFuncDesc(agg.ctx, ast.AggFuncFirstRow, []expression.Expression{gbyExpr}, false) + if err != nil { + return nil, err + } + newAgg.AggFuncs = append(newAgg.AggFuncs, firstRow) } newAgg.collectGroupByColumns() tmpSchema := expression.NewSchema(newAgg.groupByCols...) @@ -350,13 +356,13 @@ func (a *aggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, u if tmpSchema.ColumnsIndices(key) != nil { if ok, proj := ConvertAggToProj(newAgg, newAgg.schema); ok { proj.SetChildren(unionChild) - return proj + return proj, nil } break } } newAgg.SetChildren(unionChild) - return newAgg + return newAgg, nil } func (a *aggregationPushDownSolver) optimize(ctx context.Context, p LogicalPlan) (LogicalPlan, error) { @@ -430,7 +436,10 @@ func (a *aggregationPushDownSolver) aggPushDown(p LogicalPlan) (_ LogicalPlan, e pushedAgg := a.splitPartialAgg(agg) newChildren := make([]LogicalPlan, 0, len(union.children)) for _, child := range union.children { - newChild := a.pushAggCrossUnion(pushedAgg, union.Schema(), child) + newChild, err := a.pushAggCrossUnion(pushedAgg, union.Schema(), child) + if err != nil { + return p, err + } newChildren = append(newChildren, newChild) } union.SetSchema(expression.NewSchema(newChildren[0].Schema().Columns...)) diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index c7ff0b9c293d9..d0b9626f8d13e 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -1554,14 +1554,14 @@ { "SQL": "select /*+ HASH_AGG(), AGG_TO_COP() */ sum(distinct b) from pt;", "Plan": [ - "HashAgg_11 1.00 root funcs:sum(distinct Column#7)->Column#4", - "└─Projection_23 16000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#7", + "HashAgg_11 1.00 root funcs:sum(distinct Column#9)->Column#4", + "└─Projection_23 16000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#9", " └─Union_12 16000.00 root ", - " ├─HashAgg_16 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", + " ├─HashAgg_16 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", " │ └─TableReader_17 8000.00 root data:HashAgg_13", " │ └─HashAgg_13 8000.00 cop[tikv] group by:test.pt.b, ", " │ └─TableFullScan_15 10000.00 cop[tikv] table:pt, partition:p0 keep order:false, stats:pseudo", - " └─HashAgg_21 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", + " └─HashAgg_21 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", " └─TableReader_22 8000.00 root data:HashAgg_18", " └─HashAgg_18 8000.00 cop[tikv] group by:test.pt.b, ", " └─TableFullScan_20 10000.00 cop[tikv] table:pt, partition:p1 keep order:false, stats:pseudo" @@ -1575,11 +1575,11 @@ "Plan": [ "HashAgg_14 1.00 root funcs:count(distinct Column#5)->Column#6", "└─Union_15 16000.00 root ", - " ├─HashAgg_19 8000.00 root group by:test.ta.a, funcs:firstrow(test.ta.a)->Column#5", + " ├─HashAgg_19 8000.00 root group by:test.ta.a, funcs:firstrow(test.ta.a)->Column#5, funcs:firstrow(test.ta.a)->Column#5", " │ └─TableReader_20 8000.00 root data:HashAgg_16", " │ └─HashAgg_16 8000.00 cop[tikv] group by:test.ta.a, ", " │ └─TableFullScan_18 10000.00 cop[tikv] table:ta keep order:false, stats:pseudo", - " └─HashAgg_24 8000.00 root group by:test.tb.a, funcs:firstrow(test.tb.a)->Column#5", + " └─HashAgg_24 8000.00 root group by:test.tb.a, funcs:firstrow(test.tb.a)->Column#5, funcs:firstrow(test.tb.a)->Column#5", " └─TableReader_25 8000.00 root data:HashAgg_21", " └─HashAgg_21 8000.00 cop[tikv] group by:test.tb.a, ", " └─TableFullScan_23 10000.00 cop[tikv] table:tb keep order:false, stats:pseudo" @@ -1683,14 +1683,14 @@ { "SQL": "select /*+ HASH_AGG(), AGG_TO_COP() */ sum(distinct b) from pt;", "Plan": [ - "HashAgg_11 1.00 root funcs:sum(distinct Column#7)->Column#4", - "└─Projection_23 16000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#7", + "HashAgg_11 1.00 root funcs:sum(distinct Column#9)->Column#4", + "└─Projection_23 16000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#9", " └─Union_12 16000.00 root ", - " ├─HashAgg_16 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", + " ├─HashAgg_16 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", " │ └─TableReader_17 8000.00 root data:HashAgg_13", " │ └─HashAgg_13 8000.00 cop[tikv] group by:test.pt.b, ", " │ └─TableFullScan_15 10000.00 cop[tikv] table:pt, partition:p0 keep order:false, stats:pseudo", - " └─HashAgg_21 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", + " └─HashAgg_21 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", " └─TableReader_22 8000.00 root data:HashAgg_18", " └─HashAgg_18 8000.00 cop[tikv] group by:test.pt.b, ", " └─TableFullScan_20 10000.00 cop[tikv] table:pt, partition:p1 keep order:false, stats:pseudo" @@ -1704,11 +1704,11 @@ "Plan": [ "HashAgg_14 1.00 root funcs:count(distinct Column#5)->Column#6", "└─Union_15 16000.00 root ", - " ├─HashAgg_19 8000.00 root group by:test.ta.a, funcs:firstrow(test.ta.a)->Column#5", + " ├─HashAgg_19 8000.00 root group by:test.ta.a, funcs:firstrow(test.ta.a)->Column#5, funcs:firstrow(test.ta.a)->Column#5", " │ └─TableReader_20 8000.00 root data:HashAgg_16", " │ └─HashAgg_16 8000.00 cop[tikv] group by:test.ta.a, ", " │ └─TableFullScan_18 10000.00 cop[tikv] table:ta keep order:false, stats:pseudo", - " └─HashAgg_24 8000.00 root group by:test.tb.a, funcs:firstrow(test.tb.a)->Column#5", + " └─HashAgg_24 8000.00 root group by:test.tb.a, funcs:firstrow(test.tb.a)->Column#5, funcs:firstrow(test.tb.a)->Column#5", " └─TableReader_25 8000.00 root data:HashAgg_21", " └─HashAgg_21 8000.00 cop[tikv] group by:test.tb.a, ", " └─TableFullScan_23 10000.00 cop[tikv] table:tb keep order:false, stats:pseudo" diff --git a/planner/core/testdata/plan_suite_unexported_out.json b/planner/core/testdata/plan_suite_unexported_out.json index 1dfddc0a0d266..490b04077f4a0 100644 --- a/planner/core/testdata/plan_suite_unexported_out.json +++ b/planner/core/testdata/plan_suite_unexported_out.json @@ -16,7 +16,7 @@ "Join{DataScan(a)->Aggr(sum(test.t.a),firstrow(test.t.c))->DataScan(b)}(test.t.c,test.t.c)->Aggr(sum(Column#26))->Projection", "Join{DataScan(a)->Aggr(sum(test.t.a),firstrow(test.t.c))->DataScan(b)}(test.t.c,test.t.c)->Aggr(sum(Column#26))->Projection", "DataScan(t)->Aggr(sum(test.t.a))->Projection", - "UnionAll{DataScan(a)->Projection->Aggr(sum(test.t.c))->DataScan(b)->Projection->Aggr(sum(test.t.a))->DataScan(c)->Projection->Aggr(sum(test.t.b))}->Aggr(sum(Column#40))->Projection", + "UnionAll{DataScan(a)->Projection->Aggr(sum(test.t.c),firstrow(test.t.d))->DataScan(b)->Projection->Aggr(sum(test.t.a),firstrow(test.t.b))->DataScan(c)->Projection->Aggr(sum(test.t.b),firstrow(test.t.e))}->Aggr(sum(Column#40))->Projection", "Join{DataScan(a)->DataScan(b)->Aggr(max(test.t.b),firstrow(test.t.c))}(test.t.c,test.t.c)->Projection->Projection", "Join{DataScan(a)->DataScan(b)}(test.t.a,test.t.a)->Aggr(max(test.t.b),max(test.t.b))->Projection", "UnionAll{DataScan(a)->Projection->Projection->Projection->DataScan(b)->Projection->Projection->Projection}->Aggr(max(Column#38))->Projection", @@ -24,7 +24,7 @@ "Join{DataScan(t1)->DataScan(t2)}(test.t.a,test.t.a)->Projection->Projection", "UnionAll{DataScan(t1)->Projection->Aggr(count(test.t.a),sum(test.t.a))->DataScan(t2)->Projection->Aggr(count(test.t.a),sum(test.t.a))}->Aggr(avg(Column#38, Column#39))->Projection", "UnionAll{DataScan(t1)->Projection->Projection->Projection->DataScan(t2)->Projection->Projection->Projection}->Aggr(count(distinct Column#25))->Projection", - "UnionAll{DataScan(t1)->Projection->Aggr(firstrow(test.t.b))->DataScan(t2)->Projection->Aggr(firstrow(test.t.b))}->Aggr(count(distinct Column#26))->Projection" + "UnionAll{DataScan(t1)->Projection->Aggr(firstrow(test.t.b),firstrow(test.t.b))->DataScan(t2)->Projection->Aggr(firstrow(test.t.b),firstrow(test.t.b))}->Aggr(count(distinct Column#26))->Projection" ] }, { From 38d63c2fa5463a33f37d0840580e94ed23fe3bee Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Thu, 21 May 2020 15:03:22 +0800 Subject: [PATCH 33/74] planner,table: optimize partition pruning performance for range columns (#17249) --- planner/core/rule_partition_processor.go | 40 +++++------- session/bench_test.go | 33 ++++++++++ table/tables/partition.go | 77 ++++++++++++++++++------ 3 files changed, 109 insertions(+), 41 deletions(-) diff --git a/planner/core/rule_partition_processor.go b/planner/core/rule_partition_processor.go index f2c01d54274c3..5997bb957cf4c 100644 --- a/planner/core/rule_partition_processor.go +++ b/planner/core/rule_partition_processor.go @@ -15,7 +15,6 @@ package core import ( "context" "sort" - "strings" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" @@ -331,9 +330,14 @@ func intersectionRange(start, end, newStart, newEnd int) (int, int) { } func (s *partitionProcessor) pruneRangePartition(ds *DataSource, pi *model.PartitionInfo) (LogicalPlan, error) { + partExpr, err := ds.table.(partitionTable).PartitionExpr() + if err != nil { + return nil, err + } + // Partition by range columns. if len(pi.Columns) > 0 { - return s.pruneRangeColumnsPartition(ds, pi) + return s.pruneRangeColumnsPartition(ds, pi, partExpr) } // Partition by range. @@ -345,10 +349,6 @@ func (s *partitionProcessor) pruneRangePartition(ds *DataSource, pi *model.Parti result := fullRange(len(pi.Definitions)) // Extract the partition column, if the column is not null, it's possible to prune. if col != nil { - partExpr, err := ds.table.(partitionTable).PartitionExpr() - if err != nil { - return nil, err - } pruner := rangePruner{ lessThan: lessThanDataInt{ data: partExpr.ForRangePruning.LessThan, @@ -739,7 +739,7 @@ func (s *partitionProcessor) makeUnionAllChildren(ds *DataSource, pi *model.Part return unionAll, nil } -func (s *partitionProcessor) pruneRangeColumnsPartition(ds *DataSource, pi *model.PartitionInfo) (LogicalPlan, error) { +func (s *partitionProcessor) pruneRangeColumnsPartition(ds *DataSource, pi *model.PartitionInfo, pe *tables.PartitionExpr) (LogicalPlan, error) { result := fullRange(len(pi.Definitions)) if len(pi.Columns) != 1 { @@ -747,7 +747,7 @@ func (s *partitionProcessor) pruneRangeColumnsPartition(ds *DataSource, pi *mode return s.makeUnionAllChildren(ds, pi, result) } - pruner, err := makeRangeColumnPruner(ds, pi) + pruner, err := makeRangeColumnPruner(ds, pe.ForRangeColumnsPruning) if err == nil { result = partitionRangeForCNFExpr(ds.ctx, ds.allConds, pruner, result) } @@ -763,28 +763,20 @@ type rangeColumnPruner struct { maxvalue bool } -func makeRangeColumnPruner(ds *DataSource, pi *model.PartitionInfo) (*rangeColumnPruner, error) { - var maxValue bool - data := make([]expression.Expression, len(pi.Definitions)) +func makeRangeColumnPruner(ds *DataSource, from *tables.ForRangeColumnsPruning) (*rangeColumnPruner, error) { schema := expression.NewSchema(ds.TblCols...) - exprs, err := expression.ParseSimpleExprsWithNames(ds.ctx, pi.Columns[0].L, schema, ds.names) + expr, err := expression.RewriteSimpleExprWithNames(ds.ctx, from.Column, schema, ds.names) if err != nil { return nil, err } - partCol := exprs[0].(*expression.Column) - for i := 0; i < len(pi.Definitions); i++ { - if strings.EqualFold(pi.Definitions[i].LessThan[0], "MAXVALUE") { - // Use a bool flag instead of math.MaxInt64 to avoid the corner cases. - maxValue = true - } else { - tmp, err := expression.ParseSimpleExprsWithNames(ds.ctx, pi.Definitions[i].LessThan[0], schema, ds.names) - if err != nil { - return nil, err - } - data[i] = tmp[0] + partCol := expr.(*expression.Column) + data := make([]expression.Expression, len(from.LessThan)) + for i := 0; i < len(from.LessThan); i++ { + if from.LessThan[i] != nil { + data[i] = from.LessThan[i].Clone() } } - return &rangeColumnPruner{data, partCol, maxValue}, nil + return &rangeColumnPruner{data, partCol, from.MaxValue}, nil } func (p *rangeColumnPruner) fullRange() partitionRangeOR { diff --git a/session/bench_test.go b/session/bench_test.go index ccee3f316dc1b..59eef259835a9 100644 --- a/session/bench_test.go +++ b/session/bench_test.go @@ -18,6 +18,7 @@ import ( "fmt" "math/rand" "strconv" + "strings" "testing" "time" @@ -1482,3 +1483,35 @@ partition p1023 values less than (738538) } b.StopTimer() } + +func BenchmarkRangeColumnPartitionPruning(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + + var build strings.Builder + build.WriteString(`create table t (id int, dt date) partition by range columns (dt) (`) + start := time.Date(2020, 5, 15, 0, 0, 0, 0, time.UTC) + for i := 0; i < 1023; i++ { + start = start.Add(24 * time.Hour) + fmt.Fprintf(&build, "partition p%d values less than ('%s'),\n", i, start.Format("2006-01-02")) + } + build.WriteString("partition p1023 values less than maxvalue)") + mustExecute(se, build.String()) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where dt > '2020-05-01' and dt < '2020-06-07'") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0]) + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() +} diff --git a/table/tables/partition.go b/table/tables/partition.go index 88c4d300c56bd..f472964058eaf 100644 --- a/table/tables/partition.go +++ b/table/tables/partition.go @@ -117,6 +117,48 @@ type PartitionExpr struct { Expr expression.Expression // Used in the range pruning process. *ForRangePruning + // Used in the range column pruning process. + *ForRangeColumnsPruning +} + +// ForRangeColumnsPruning is used for range partition pruning. +type ForRangeColumnsPruning struct { + LessThan []expression.Expression + Column ast.ExprNode + MaxValue bool +} + +func dataForRangeColumnsPruning(ctx sessionctx.Context, pi *model.PartitionInfo, schema *expression.Schema, names []*types.FieldName, p *parser.Parser) (*ForRangeColumnsPruning, error) { + col, err := parseExpr(p, pi.Columns[0].L) + if err != nil { + return nil, errors.Trace(err) + } + var res ForRangeColumnsPruning + res.Column = col + res.LessThan = make([]expression.Expression, len(pi.Definitions)) + for i := 0; i < len(pi.Definitions); i++ { + if strings.EqualFold(pi.Definitions[i].LessThan[0], "MAXVALUE") { + // Use a bool flag instead of math.MaxInt64 to avoid the corner cases. + res.MaxValue = true + } else { + tmp, err := parseSimpleExprWithNames(p, ctx, pi.Definitions[i].LessThan[0], schema, names) + if err != nil { + return nil, err + } + res.LessThan[i] = tmp + } + } + return &res, nil +} + +// parseSimpleExprWithNames parses simple expression string to Expression. +// The expression string must only reference the column in the given NameSlice. +func parseSimpleExprWithNames(p *parser.Parser, ctx sessionctx.Context, exprStr string, schema *expression.Schema, names types.NameSlice) (expression.Expression, error) { + exprNode, err := parseExpr(p, exprStr) + if err != nil { + return nil, errors.Trace(err) + } + return expression.RewriteSimpleExprWithNames(ctx, exprNode, schema, names) } // ForRangePruning is used for range partition pruning. @@ -180,6 +222,7 @@ func generateRangePartitionExpr(ctx sessionctx.Context, pi *model.PartitionInfo, var buf bytes.Buffer schema := expression.NewSchema(columns...) partStr := rangePartitionString(pi) + p := parser.New() for i := 0; i < len(pi.Definitions); i++ { if strings.EqualFold(pi.Definitions[i].LessThan[0], "MAXVALUE") { // Expr less than maxvalue is always true. @@ -188,24 +231,34 @@ func generateRangePartitionExpr(ctx sessionctx.Context, pi *model.PartitionInfo, fmt.Fprintf(&buf, "((%s) < (%s))", partStr, pi.Definitions[i].LessThan[0]) } - exprs, err := expression.ParseSimpleExprsWithNames(ctx, buf.String(), schema, names) + expr, err := parseSimpleExprWithNames(p, ctx, buf.String(), schema, names) if err != nil { // If it got an error here, ddl may hang forever, so this error log is important. logutil.BgLogger().Error("wrong table partition expression", zap.String("expression", buf.String()), zap.Error(err)) return nil, errors.Trace(err) } - locateExprs = append(locateExprs, exprs[0]) + locateExprs = append(locateExprs, expr) buf.Reset() } ret := &PartitionExpr{ UpperBounds: locateExprs, } - if len(pi.Columns) == 0 { + + switch len(pi.Columns) { + case 0: tmp, err := dataForRangePruning(pi) if err != nil { return nil, errors.Trace(err) } ret.ForRangePruning = tmp + case 1: + tmp, err := dataForRangeColumnsPruning(ctx, pi, schema, names, p) + if err != nil { + return nil, errors.Trace(err) + } + ret.ForRangeColumnsPruning = tmp + default: + panic("range column partition currently support only one column") } return ret, nil } @@ -214,7 +267,7 @@ func generateHashPartitionExpr(ctx sessionctx.Context, pi *model.PartitionInfo, columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { // The caller should assure partition info is not nil. schema := expression.NewSchema(columns...) - origExpr, err := parsePartitionExpr(ctx, pi.Expr, schema, names) + origExpr, err := parseExpr(parser.New(), pi.Expr) if err != nil { return nil, err } @@ -233,14 +286,7 @@ func generateHashPartitionExpr(ctx sessionctx.Context, pi *model.PartitionInfo, // PartitionExpr returns the partition expression. func (t *partitionedTable) PartitionExpr() (*PartitionExpr, error) { - pi := t.meta.GetPartitionInfo() - switch pi.Type { - case model.PartitionTypeHash: - return t.partitionExpr, nil - case model.PartitionTypeRange: - return t.partitionExpr, nil - } - panic("cannot reach here") + return t.partitionExpr, nil } func partitionRecordKey(pid int64, handle int64) kv.Key { @@ -463,12 +509,9 @@ func FindPartitionByName(meta *model.TableInfo, parName string) (int64, error) { return -1, errors.Trace(table.ErrUnknownPartition.GenWithStackByArgs(parName, meta.Name.O)) } -func parsePartitionExpr(ctx sessionctx.Context, exprStr string, schema *expression.Schema, names types.NameSlice) (ast.ExprNode, error) { +func parseExpr(p *parser.Parser, exprStr string) (ast.ExprNode, error) { exprStr = "select " + exprStr - stmts, warns, err := parser.New().Parse(exprStr, "", "") - for _, warn := range warns { - ctx.GetSessionVars().StmtCtx.AppendWarning(util.SyntaxWarn(warn)) - } + stmts, _, err := p.Parse(exprStr, "", "") if err != nil { return nil, util.SyntaxWarn(err) } From 0d95b09e8aea02ec96801a21543489f1de240088 Mon Sep 17 00:00:00 2001 From: Chengpeng Yan <41809508+Reminiscent@users.noreply.github.com> Date: Thu, 21 May 2020 19:26:00 +0800 Subject: [PATCH 34/74] executor: Remove unnecessary information in explain analyze output (#16248) --- executor/aggregate.go | 6 ++++-- executor/index_lookup_hash_join.go | 3 ++- executor/index_lookup_join.go | 3 ++- executor/index_lookup_merge_join.go | 3 ++- executor/join.go | 3 ++- executor/projection.go | 5 +++-- executor/shuffle.go | 3 ++- util/execdetails/execdetails.go | 21 +++++++++++++++------ 8 files changed, 32 insertions(+), 15 deletions(-) diff --git a/executor/aggregate.go b/executor/aggregate.go index ba118fb2e8bbc..82e11199601e6 100644 --- a/executor/aggregate.go +++ b/executor/aggregate.go @@ -31,6 +31,7 @@ import ( "github.com/pingcap/tidb/types/json" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/util/execdetails" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/memory" "github.com/pingcap/tidb/util/set" @@ -243,8 +244,9 @@ func (e *HashAggExec) Close() error { partialConcurrency = cap(e.partialWorkers) finalConcurrency = cap(e.finalWorkers) } - e.runtimeStats.SetConcurrencyInfo("PartialConcurrency", partialConcurrency) - e.runtimeStats.SetConcurrencyInfo("FinalConcurrency", finalConcurrency) + partialConcurrencyInfo := execdetails.NewConcurrencyInfo("PartialConcurrency", partialConcurrency) + finalConcurrencyInfo := execdetails.NewConcurrencyInfo("FinalConcurrency", finalConcurrency) + e.runtimeStats.SetConcurrencyInfo(partialConcurrencyInfo, finalConcurrencyInfo) } return e.baseExecutor.Close() } diff --git a/executor/index_lookup_hash_join.go b/executor/index_lookup_hash_join.go index f3dbf8560c203..5ee933b5dc0a3 100644 --- a/executor/index_lookup_hash_join.go +++ b/executor/index_lookup_hash_join.go @@ -25,6 +25,7 @@ import ( "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/util/execdetails" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/memory" "github.com/pingcap/tidb/util/ranger" @@ -295,7 +296,7 @@ func (e *IndexNestedLoopHashJoin) Close() error { } if e.runtimeStats != nil { concurrency := cap(e.joinChkResourceCh) - e.runtimeStats.SetConcurrencyInfo("Concurrency", concurrency) + e.runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", concurrency)) } for i := range e.joinChkResourceCh { close(e.joinChkResourceCh[i]) diff --git a/executor/index_lookup_join.go b/executor/index_lookup_join.go index ec7650ccf5ffb..14890aa716dc9 100644 --- a/executor/index_lookup_join.go +++ b/executor/index_lookup_join.go @@ -32,6 +32,7 @@ import ( "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/util/execdetails" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/memory" "github.com/pingcap/tidb/util/mvmap" @@ -681,7 +682,7 @@ func (e *IndexLookUpJoin) Close() error { e.memTracker = nil if e.runtimeStats != nil { concurrency := cap(e.resultCh) - e.runtimeStats.SetConcurrencyInfo("Concurrency", concurrency) + e.runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", concurrency)) } return e.baseExecutor.Close() } diff --git a/executor/index_lookup_merge_join.go b/executor/index_lookup_merge_join.go index 79b993495dc9d..648bd2cb4e783 100644 --- a/executor/index_lookup_merge_join.go +++ b/executor/index_lookup_merge_join.go @@ -26,6 +26,7 @@ import ( "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/util/execdetails" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/memory" "github.com/pingcap/tidb/util/ranger" @@ -694,7 +695,7 @@ func (e *IndexLookUpMergeJoin) Close() error { e.memTracker = nil if e.runtimeStats != nil { concurrency := cap(e.resultCh) - e.runtimeStats.SetConcurrencyInfo("Concurrency", concurrency) + e.runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", concurrency)) } return e.baseExecutor.Close() } diff --git a/executor/join.go b/executor/join.go index fb94c34b8bb36..f22eb62648c2c 100644 --- a/executor/join.go +++ b/executor/join.go @@ -31,6 +31,7 @@ import ( "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/codec" "github.com/pingcap/tidb/util/disk" + "github.com/pingcap/tidb/util/execdetails" "github.com/pingcap/tidb/util/memory" "github.com/pingcap/tidb/util/stringutil" ) @@ -138,7 +139,7 @@ func (e *HashJoinExec) Close() error { if e.runtimeStats != nil { concurrency := cap(e.joiners) - e.runtimeStats.SetConcurrencyInfo("Concurrency", concurrency) + e.runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", concurrency)) if e.rowContainer != nil { e.runtimeStats.SetAdditionalInfo(e.rowContainer.stat.String()) } diff --git a/executor/projection.go b/executor/projection.go index 7c855451e2b84..920d82f8d2e52 100644 --- a/executor/projection.go +++ b/executor/projection.go @@ -24,6 +24,7 @@ import ( "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/util/execdetails" "github.com/pingcap/tidb/util/logutil" "github.com/pingcap/tidb/util/memory" "go.uber.org/zap" @@ -308,9 +309,9 @@ func (e *ProjectionExec) Close() error { } if e.runtimeStats != nil { if e.isUnparallelExec() { - e.runtimeStats.SetConcurrencyInfo("Concurrency", 0) + e.runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", 0)) } else { - e.runtimeStats.SetConcurrencyInfo("Concurrency", int(e.numWorkers)) + e.runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", int(e.numWorkers))) } } return e.baseExecutor.Close() diff --git a/executor/shuffle.go b/executor/shuffle.go index 84ec4022018e8..c8f93bafb4bf7 100644 --- a/executor/shuffle.go +++ b/executor/shuffle.go @@ -22,6 +22,7 @@ import ( "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/util/execdetails" "github.com/pingcap/tidb/util/logutil" "github.com/spaolacci/murmur3" "go.uber.org/zap" @@ -144,7 +145,7 @@ func (e *ShuffleExec) Close() error { e.executed = false if e.runtimeStats != nil { - e.runtimeStats.SetConcurrencyInfo("ShuffleConcurrency", e.concurrency) + e.runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("ShuffleConcurrency", e.concurrency)) } err := e.dataSource.Close() diff --git a/util/execdetails/execdetails.go b/util/execdetails/execdetails.go index f126b9821a858..c2a516e3dbcec 100644 --- a/util/execdetails/execdetails.go +++ b/util/execdetails/execdetails.go @@ -360,12 +360,17 @@ type RuntimeStatsColl struct { readerStats map[string]*ReaderRuntimeStats } -// concurrencyInfo is used to save the concurrency information of the executor operator -type concurrencyInfo struct { +// ConcurrencyInfo is used to save the concurrency information of the executor operator +type ConcurrencyInfo struct { concurrencyName string concurrencyNum int } +// NewConcurrencyInfo creates new executor's concurrencyInfo. +func NewConcurrencyInfo(name string, num int) *ConcurrencyInfo { + return &ConcurrencyInfo{name, num} +} + // RuntimeStats collects one executor's execution info. type RuntimeStats struct { // executor's Next() called times. @@ -378,7 +383,7 @@ type RuntimeStats struct { // protect concurrency mu sync.Mutex // executor concurrency information - concurrency []concurrencyInfo + concurrency []*ConcurrencyInfo // additional information for executors additionalInfo string @@ -466,12 +471,16 @@ func (e *RuntimeStats) SetRowNum(rowNum int64) { atomic.StoreInt64(&e.rows, rowNum) } -// SetConcurrencyInfo sets the concurrency information. +// SetConcurrencyInfo sets the concurrency informations. +// We must clear the concurrencyInfo first when we call the SetConcurrencyInfo. // When the num <= 0, it means the exector operator is not executed parallel. -func (e *RuntimeStats) SetConcurrencyInfo(name string, num int) { +func (e *RuntimeStats) SetConcurrencyInfo(infos ...*ConcurrencyInfo) { e.mu.Lock() defer e.mu.Unlock() - e.concurrency = append(e.concurrency, concurrencyInfo{concurrencyName: name, concurrencyNum: num}) + e.concurrency = e.concurrency[:0] + for _, info := range infos { + e.concurrency = append(e.concurrency, info) + } } // SetAdditionalInfo sets the additional information. From d5aaaae1b2db05ad3f4672503f99792c03e6badf Mon Sep 17 00:00:00 2001 From: "Zhuomin(Charming) Liu" Date: Thu, 21 May 2020 20:16:54 +0800 Subject: [PATCH 35/74] tikv: fix region cache do not filter the down peers (#17337) --- go.mod | 24 +++--- go.sum | 133 ++++++++++++++++++++++++++++++++- server/http_handler.go | 12 +-- store/mockstore/mocktikv/pd.go | 12 +-- store/tikv/pd_codec.go | 32 ++++---- store/tikv/region_cache.go | 60 ++++++++++----- store/tikv/store_test.go | 12 +-- 7 files changed, 219 insertions(+), 66 deletions(-) diff --git a/go.mod b/go.mod index e97fbb4c5fbb9..a412bfea2a61c 100644 --- a/go.mod +++ b/go.mod @@ -13,34 +13,34 @@ require ( github.com/golang/protobuf v1.3.4 github.com/golang/snappy v0.0.1 github.com/google/btree v1.0.0 - github.com/google/pprof v0.0.0-20190930153522-6ce02741cba3 + github.com/google/pprof v0.0.0-20200407044318-7d83b28da2e9 github.com/google/uuid v1.1.1 github.com/gorilla/mux v1.7.3 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 github.com/klauspost/cpuid v1.2.1 github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7 github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef - github.com/ngaut/unistore v0.0.0-20200515074342-406feebf3ca9 + github.com/ngaut/unistore v0.0.0-20200521040325-2af94f1b0c83 github.com/opentracing/basictracer-go v1.0.0 github.com/opentracing/opentracing-go v1.1.0 - github.com/pingcap/br v0.0.0-20200426093517-dd11ae28b885 + github.com/pingcap/br v0.0.0-20200521085655-53201addd4ad github.com/pingcap/check v0.0.0-20200212061837-5e12011dc712 github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011 - github.com/pingcap/failpoint v0.0.0-20200210140405-f8f9fb234798 + github.com/pingcap/failpoint v0.0.0-20200506114213-c17f16071c53 github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 - github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677 + github.com/pingcap/kvproto v0.0.0-20200518112156-d4aeb467de29 github.com/pingcap/log v0.0.0-20200511115504-543df19646ad github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1 - github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 + github.com/pingcap/pd/v4 v4.0.0-rc.2.0.20200520083007-2c251bd8f181 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 - github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible + github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200514040632-f76b3e428e19+incompatible github.com/pingcap/tipb v0.0.0-20200417094153-7316d94df1ee - github.com/prometheus/client_golang v1.0.0 - github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 - github.com/prometheus/common v0.4.1 + github.com/prometheus/client_golang v1.5.1 + github.com/prometheus/client_model v0.2.0 + github.com/prometheus/common v0.9.1 github.com/shirou/gopsutil v2.19.10+incompatible - github.com/sirupsen/logrus v1.4.2 + github.com/sirupsen/logrus v1.6.0 github.com/soheilhy/cmux v0.1.4 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 github.com/tiancaiamao/appdash v0.0.0-20181126055449-889f96f722a2 @@ -55,7 +55,7 @@ require ( golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd golang.org/x/text v0.3.2 golang.org/x/tools v0.0.0-20200325203130-f53864d0dba1 - google.golang.org/grpc v1.25.1 + google.golang.org/grpc v1.26.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 honnef.co/go/tools v0.0.1-2020.1.4 // indirect sourcegraph.com/sourcegraph/appdash v0.0.0-20180531100431-4c381bd170b4 diff --git a/go.sum b/go.sum index bb4dd00a30817..d1f27831724ae 100644 --- a/go.sum +++ b/go.sum @@ -6,14 +6,23 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0 h1:0E3eE8MX426vUOs7aHfI7aN1BrIzzzf4ccKCSfSjGmc= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0 h1:9/vpR43S4aJaROxqQHQ3nH9lfyKKV0dC3vOmnw8ebQQ= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.4.0 h1:KDdqY5VTXBTqpSbctVTt0mVvfanP6JZzNzLE0qNY100= cloud.google.com/go/storage v1.4.0/go.mod h1:ZusYJWlOshgSBGbt6K3GnB3MT3H1xs2id9+TCl4fDBA= +cloud.google.com/go/storage v1.5.0 h1:RPUcBvDeYgQFMfQu1eBMq6piD1SXmLH+vK3qjewZPus= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -31,12 +40,15 @@ github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmx github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/appleboy/gin-jwt/v2 v2.6.3/go.mod h1:MfPYA4ogzvOcVkRwAxT7quHOtQmVKDpTwxyUrC2DNw0= github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.26.1 h1:JGQggXhOiNJIqsmbYUl3cYtJZUffeOWlHtxfzGK7WPI= github.com/aws/aws-sdk-go v1.26.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.30.24 h1:y3JPD51VuEmVqN3BEDVm4amGpDma2cKJcDPuAU1OR58= +github.com/aws/aws-sdk-go v1.30.24/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -44,13 +56,19 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blacktear23/go-proxyprotocol v0.0.0-20180807104634-af7a81e8dd0d h1:rQlvB2AYWme2bIB18r/SipGiMEVJYE9U0z+MGoU/LtQ= github.com/blacktear23/go-proxyprotocol v0.0.0-20180807104634-af7a81e8dd0d/go.mod h1:VKt7CNAQxpFpSDz3sXyj9hY/GbVsQCr0sB3w59nE7lU= +github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheggaaa/pb/v3 v3.0.1 h1:m0BngUk2LuSRYdx4fujDKNRXNDpbNCfptPfVT2m6OJY= github.com/cheggaaa/pb/v3 v3.0.1/go.mod h1:SqqeMF/pMOIu3xgGoxtPYhMNQP258xE4x/XRTYua+KU= +github.com/cheggaaa/pb/v3 v3.0.4 h1:QZEPYOj2ix6d5oEg63fbHmpolrnNiwjUsk+h74Yt4bM= +github.com/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20171208011716-f6d7a1f6fbf3/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y= @@ -63,6 +81,7 @@ github.com/coocood/bbloom v0.0.0-20190830030839-58deb6228d64 h1:W1SHiII3e0jVwvaQ github.com/coocood/bbloom v0.0.0-20190830030839-58deb6228d64/go.mod h1:F86k/6c7aDUdwSUevnLpHS/3Q9hzYCE99jGk2xsHnt0= github.com/coocood/rtutil v0.0.0-20190304133409-c84515f646f2 h1:NnLfQ77q0G4k2Of2c1ceQ0ec6MkLQyDp+IGdVM0D8XM= github.com/coocood/rtutil v0.0.0-20190304133409-c84515f646f2/go.mod h1:7qG7YFnOALvsx6tKTNmQot8d7cGFXM9TidzvRFLWYwM= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -70,13 +89,16 @@ github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmf github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/corona10/goimagehash v1.0.2/go.mod h1:/l9umBhvcHQXVtQO1V6Gp1yD20STawkhRnnX0D1bvVI= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= @@ -99,6 +121,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -108,16 +131,20 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsouza/fake-gcs-server v1.15.0 h1:ss/ztlt10Y64A5qslmxZKsiqW/i28t5DkRtv6qSFaLQ= github.com/fsouza/fake-gcs-server v1.15.0/go.mod h1:HNxAJ/+FY/XSsxuwz8iIYdp2GtMmPbJ8WQjjGMxd6Qk= +github.com/fsouza/fake-gcs-server v1.17.0 h1:OeH75kBZcZa3ZE+zz/mFdJ2btt9FgqfjI7gIh9+5fvk= +github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= @@ -130,8 +157,11 @@ github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmC github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= @@ -158,18 +188,23 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/goccy/go-graphviz v0.0.5/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -189,6 +224,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -196,6 +233,8 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190930153522-6ce02741cba3 h1:3CYI9xg87xNAD+es02gZxbX/ky4KQeoFBsNOzuoAQZg= github.com/google/pprof v0.0.0-20190930153522-6ce02741cba3/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20200407044318-7d83b28da2e9 h1:K+lX49/3eURCE1IjlaZN//u6c+9nfDAMnyQ9E2dsJbY= +github.com/google/pprof v0.0.0-20200407044318-7d83b28da2e9/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -204,16 +243,20 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= @@ -224,13 +267,18 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hypnoglow/gormzap v0.3.0/go.mod h1:5Wom8B7Jl2oK0Im9hs6KQ+Kl92w4Y7gKCrj66rhyvw0= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -242,6 +290,8 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= @@ -269,6 +319,8 @@ github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -292,6 +344,7 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -319,13 +372,15 @@ github.com/montanaflynn/stats v0.5.0/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFW github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncw/directio v1.0.4 h1:CojwI07mCEmRkajgx42Pf8jyCwTs1ji9/Ij9/PJG12k= github.com/ncw/directio v1.0.4/go.mod h1:CKGdcN7StAaqjT7Qack3lAXeX4pjnyc46YeqZH1yWVY= +github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7 h1:7KAv7KMGTTqSmYZtNdcNTgsos+vFzULLwyElndwn+5c= github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7/go.mod h1:iWMfgwqYW+e8n5lC/jjNEhwcjbRDpl5NT7n2h+4UNcI= github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef h1:K0Fn+DoFqNqktdZtdV3bPQ/0cuYh2H4rkg0tytX/07k= github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef/go.mod h1:7WjlapSfwQyo6LNmIvEWzsW1hbBQfpUO4JWnuQRmva8= -github.com/ngaut/unistore v0.0.0-20200515074342-406feebf3ca9 h1:3jyYAfLtjYY1CMVzE5U//Wd6Qm8uPa2sCkH5nA34qKE= -github.com/ngaut/unistore v0.0.0-20200515074342-406feebf3ca9/go.mod h1:wU2ZTg5G6OToAvCdj1uXa9LxXxna033vfGfIgU4klwY= +github.com/ngaut/unistore v0.0.0-20200521040325-2af94f1b0c83 h1:jg4kakXwR1PMNCBT9JZdYbvybuUwbsEwJ+7Ssl9eT1s= +github.com/ngaut/unistore v0.0.0-20200521040325-2af94f1b0c83/go.mod h1:odn0MiR+DNxnxOiCskG4wWacBIW2GBmJOf/TAnMZfWE= github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -343,13 +398,17 @@ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsq github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.3.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d h1:U+PMnTlV2tu7RuMK5etusZG3Cf+rpow5hqQByeCzJ2g= github.com/phf/go-queue v0.0.0-20170504031614-9abe38d0371d/go.mod h1:lXfE4PvvTW5xOjO6Mba8zDPyw8M93B6AQ7frTGnMlA8= github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap-incubator/tidb-dashboard v0.0.0-20200407064406-b2b8ad403d01/go.mod h1:77fCh8d3oKzC5ceOJWeZXAS/mLzVgdZ7rKniwmOyFuo= +github.com/pingcap-incubator/tidb-dashboard v0.0.0-20200514075710-eecc9a4525b5/go.mod h1:8q+yDx0STBPri8xS4A2duS1dAf+xO0cMtjwe0t6MWJk= github.com/pingcap/br v0.0.0-20200426093517-dd11ae28b885 h1:gI14HoGBF9UyECMgqSRZx1ONhREtrZe8JCmZ/6OFilw= github.com/pingcap/br v0.0.0-20200426093517-dd11ae28b885/go.mod h1:4w3meMnk7HDNpNgjuRAxavruTeKJvUiXxoEWTjzXPnA= +github.com/pingcap/br v0.0.0-20200521085655-53201addd4ad h1:nptiQT0kWdIUghh49OyaTBYb4DtdxJmsLHOxbU25kW4= +github.com/pingcap/br v0.0.0-20200521085655-53201addd4ad/go.mod h1:SlSUHWY7QUoooiYxOKuJ8kUh2KjI29ogBh89YXz2dLA= github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= github.com/pingcap/check v0.0.0-20191107115940-caf2b9e6ccf4/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= github.com/pingcap/check v0.0.0-20191216031241-8a5a85928f12/go.mod h1:PYMCGwN0JHjoqGr3HrZoD+b8Tgx8bKnArhSq8YVzUMc= @@ -364,6 +423,8 @@ github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011/go.mod h1:Oi8TUi github.com/pingcap/failpoint v0.0.0-20191029060244-12f4ac2fd11d/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI= github.com/pingcap/failpoint v0.0.0-20200210140405-f8f9fb234798 h1:6DMbRqPI1qzQ8N1xc3+nKY8IxSACd9VqQKkRVvbyoIg= github.com/pingcap/failpoint v0.0.0-20200210140405-f8f9fb234798/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI= +github.com/pingcap/failpoint v0.0.0-20200506114213-c17f16071c53 h1:8sC8OLinmaw24xLeeJlYBFvUBsOiOYBtNqTuVOTnynQ= +github.com/pingcap/failpoint v0.0.0-20200506114213-c17f16071c53/go.mod h1:w4PEZ5y16LeofeeGwdgZB4ddv9bLyDuIX+ljstgKZyk= github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d h1:rCmRK0lCRrHMUbS99BKFYhK9YxJDNw0xB033cQbYo0s= github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d/go.mod h1:fMRU1BA1y+r89AxUoaAar4JjrhUkVDt0o0Np6V8XbDQ= github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 h1:surzm05a8C9dN8dIUmo4Be2+pMRb6f55i+UIYrluu2E= @@ -371,11 +432,15 @@ github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989/go.mod h1:O17Xtb github.com/pingcap/kvproto v0.0.0-20190227013052-e71ca0165a5f/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY= github.com/pingcap/kvproto v0.0.0-20191211054548-3c6b38ea5107/go.mod h1:WWLmULLO7l8IOcQG+t+ItJ3fEcrL5FxF0Wu+HrMy26w= github.com/pingcap/kvproto v0.0.0-20200214064158-62d31900d88e/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= +github.com/pingcap/kvproto v0.0.0-20200411081810-b85805c9476c/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= github.com/pingcap/kvproto v0.0.0-20200417092353-efbe03bcffbd/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= github.com/pingcap/kvproto v0.0.0-20200420075417-e0c6e8842f22/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= github.com/pingcap/kvproto v0.0.0-20200423020121-038e31959c2a/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= +github.com/pingcap/kvproto v0.0.0-20200424032552-6650270c39c3/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677 h1:90pbLYmkk7bXLgyaYZj22QQLouVxqTZrswi+7DNMSRQ= github.com/pingcap/kvproto v0.0.0-20200428135407-0f5ffe459677/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= +github.com/pingcap/kvproto v0.0.0-20200518112156-d4aeb467de29 h1:NpW1OuYrIl+IQrSsVbtyHpHpazmSCHy+ysrOixY0xY4= +github.com/pingcap/kvproto v0.0.0-20200518112156-d4aeb467de29/go.mod h1:IOdRDPLyda8GX2hE/jO7gqaCV/PNFh8BZQCQZXfIOqI= github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8= github.com/pingcap/log v0.0.0-20200511115504-543df19646ad h1:SveG82rmu/GFxYanffxsSF503SiQV+2JLnWEiGiF+Tc= @@ -387,14 +452,19 @@ github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1 h1:xPCqyGxvHfAZrBnf github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 h1:JTzYYukREvxVSKW/ncrzNjFitd8snoQ/Xz32pw8i+s8= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2/go.mod h1:s+utZtXDznOiL24VK0qGmtoHjjXNsscJx3m1n8cC56s= +github.com/pingcap/pd/v4 v4.0.0-rc.2.0.20200520083007-2c251bd8f181 h1:FM+PzdoR3fmWAJx3ug+p5aOgs5aZYwFkoDL7Potdsz0= +github.com/pingcap/pd/v4 v4.0.0-rc.2.0.20200520083007-2c251bd8f181/go.mod h1:q4HTx/bA8aKBa4S7L+SQKHvjRPXCRV0tA0yRw0qkZSA= github.com/pingcap/sysutil v0.0.0-20200206130906-2bfa6dc40bcd/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 h1:PI8YpTl45F8ilNkrPtT4IdbcZB1SCEa+gK/U5GJYl3E= github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1/go.mod h1:EB/852NMQ+aRKioCpToQ94Wl7fktV+FNnxf3CX/TTXI= github.com/pingcap/tidb v1.1.0-beta.0.20200424154252-5ede18f10eed/go.mod h1:m2VDlJDbUeHPCXAfKPajqLmB1uLvWpkKk3zALNqDYdw= +github.com/pingcap/tidb v1.1.0-beta.0.20200509133407-a9dc72cf2558/go.mod h1:cXNbVSQAkwwmjFQmEnEPI00Z2/Y/KOhouttUPERiInE= github.com/pingcap/tidb v1.1.0-beta.0.20200513065557-5a0787dfa915/go.mod h1:khS9Z9YlbtxsaZsSsSahelgh5L16EtP30QADFmPiI/I= github.com/pingcap/tidb-tools v4.0.0-beta.1.0.20200306084441-875bd09aa3d5+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible h1:+K5bqDYG5HT+GqLdx4GH5VmS84+xHgpHbGg6Xt6qQec= github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200421113014-507d2bb3a15e+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= +github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200514040632-f76b3e428e19+incompatible h1:/JKsYjsa5Ug8v5CN4zIbJGIqsvgBUkGwaP/rEScVvWM= +github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200514040632-f76b3e428e19+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= github.com/pingcap/tipb v0.0.0-20190428032612-535e1abaa330/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI= github.com/pingcap/tipb v0.0.0-20200417094153-7316d94df1ee h1:XJQ6/LGzOSc/jo33AD8t7jtc4GohxcyODsYnb+kZXJM= github.com/pingcap/tipb v0.0.0-20200417094153-7316d94df1ee/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI= @@ -406,18 +476,31 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 h1:HQagqIiBmr8YXawX/le3+O26N+vPPC1PtjaF3mwnook= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -426,6 +509,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44 h1:tB9NOR21++IjLyVx3/PCPhWMwqGNCMQEH96A6dMZ/gc= github.com/sergi/go-diff v1.0.1-0.20180205163309-da645544ed44/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -442,6 +526,8 @@ github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= @@ -451,12 +537,15 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -464,6 +553,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= github.com/swaggo/http-swagger v0.0.0-20200103000832-0e9263c4b516/go.mod h1:O1lAbCgAAX/KZ80LM/OXwtWFI/5TvZlwxSg8Cq08PV0= @@ -511,6 +602,7 @@ github.com/yookoala/realpath v1.0.0/go.mod h1:gJJMA9wuX7AcqLy1+ffPatSCySA1FQ2S8Y github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zhangjinpeng1987/raft v0.0.0-20190624145930-deeb32d6553d h1:rGkexfPDxNuTCObUwTbsRUlti+evR/Ksb4dKy6esXW0= github.com/zhangjinpeng1987/raft v0.0.0-20190624145930-deeb32d6553d/go.mod h1:1KDQ09J8MRHEtHze4at7BJZDW/doUAgkJ8w9KjEUhSo= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -521,6 +613,7 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -537,6 +630,7 @@ go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= @@ -557,14 +651,20 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299 h1:zQpM52jfKHG6II1ISZY1ZcpygvuSFZpLwfluuF89XOg= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -595,13 +695,17 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -609,6 +713,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -635,9 +741,15 @@ golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -672,11 +784,14 @@ golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191107010934-f79515f33823/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191114200427-caa0b0f7d508/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= @@ -690,13 +805,19 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.1 h1:5mMS6mYvK5LVB8+ujVBC33Y8gltBo/kT6HBm6kU80G4= +google.golang.org/api v0.15.1/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -707,17 +828,24 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9 h1:6XzpBoANz1NqMNfDXzc2QmHmbb1vyMsvRfoP5rM+K1I= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb h1:ADPHZzpzM4tk4V4S5cnCrr5SwzvlrPRmqqCuJDB8UTs= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v0.0.0-20180607172857-7a6a684ca69e/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/alecthomas/gometalinter.v2 v2.0.12/go.mod h1:NDRytsqEZyolNuAgTzJkZMkSQM7FIKyzVzGhjB/qfYo= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA= @@ -747,6 +875,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/server/http_handler.go b/server/http_handler.go index b85e097025eda..56fac616165ae 100644 --- a/server/http_handler.go +++ b/server/http_handler.go @@ -591,25 +591,25 @@ type RegionFrameRange struct { func (t *tikvHandlerTool) getRegionsMeta(regionIDs []uint64) ([]RegionMeta, error) { regions := make([]RegionMeta, len(regionIDs)) for i, regionID := range regionIDs { - meta, leader, err := t.RegionCache.PDClient().GetRegionByID(context.TODO(), regionID) + region, err := t.RegionCache.PDClient().GetRegionByID(context.TODO(), regionID) if err != nil { return nil, errors.Trace(err) } failpoint.Inject("errGetRegionByIDEmpty", func(val failpoint.Value) { if val.(bool) { - meta = nil + region.Meta = nil } }) - if meta == nil { + if region.Meta == nil { return nil, errors.Errorf("region not found for regionID %q", regionID) } regions[i] = RegionMeta{ ID: regionID, - Leader: leader, - Peers: meta.Peers, - RegionEpoch: meta.RegionEpoch, + Leader: region.Leader, + Peers: region.Meta.Peers, + RegionEpoch: region.Meta.RegionEpoch, } } diff --git a/store/mockstore/mocktikv/pd.go b/store/mockstore/mocktikv/pd.go index fcc93be991847..c5ca89132354f 100644 --- a/store/mockstore/mocktikv/pd.go +++ b/store/mockstore/mocktikv/pd.go @@ -91,19 +91,19 @@ func (m *mockTSFuture) Wait() (int64, int64, error) { return m.pdc.GetTS(m.ctx) } -func (c *pdClient) GetRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) { +func (c *pdClient) GetRegion(ctx context.Context, key []byte) (*pd.Region, error) { region, peer := c.cluster.GetRegionByKey(key) - return region, peer, nil + return &pd.Region{Meta: region, Leader: peer}, nil } -func (c *pdClient) GetPrevRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) { +func (c *pdClient) GetPrevRegion(ctx context.Context, key []byte) (*pd.Region, error) { region, peer := c.cluster.GetPrevRegionByKey(key) - return region, peer, nil + return &pd.Region{Meta: region, Leader: peer}, nil } -func (c *pdClient) GetRegionByID(ctx context.Context, regionID uint64) (*metapb.Region, *metapb.Peer, error) { +func (c *pdClient) GetRegionByID(ctx context.Context, regionID uint64) (*pd.Region, error) { region, peer := c.cluster.GetRegionByID(regionID) - return region, peer, nil + return &pd.Region{Meta: region, Leader: peer}, nil } func (c *pdClient) ScanRegions(ctx context.Context, startKey []byte, endKey []byte, limit int) ([]*metapb.Region, []*metapb.Peer, error) { diff --git a/store/tikv/pd_codec.go b/store/tikv/pd_codec.go index 436b929055154..8603491033cc0 100644 --- a/store/tikv/pd_codec.go +++ b/store/tikv/pd_codec.go @@ -28,23 +28,23 @@ type codecPDClient struct { // GetRegion encodes the key before send requests to pd-server and decodes the // returned StartKey && EndKey from pd-server. -func (c *codecPDClient) GetRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) { +func (c *codecPDClient) GetRegion(ctx context.Context, key []byte) (*pd.Region, error) { encodedKey := codec.EncodeBytes([]byte(nil), key) - region, peer, err := c.Client.GetRegion(ctx, encodedKey) - return processRegionResult(region, peer, err) + region, err := c.Client.GetRegion(ctx, encodedKey) + return processRegionResult(region, err) } -func (c *codecPDClient) GetPrevRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) { +func (c *codecPDClient) GetPrevRegion(ctx context.Context, key []byte) (*pd.Region, error) { encodedKey := codec.EncodeBytes([]byte(nil), key) - region, peer, err := c.Client.GetPrevRegion(ctx, encodedKey) - return processRegionResult(region, peer, err) + region, err := c.Client.GetPrevRegion(ctx, encodedKey) + return processRegionResult(region, err) } // GetRegionByID encodes the key before send requests to pd-server and decodes the // returned StartKey && EndKey from pd-server. -func (c *codecPDClient) GetRegionByID(ctx context.Context, regionID uint64) (*metapb.Region, *metapb.Peer, error) { - region, peer, err := c.Client.GetRegionByID(ctx, regionID) - return processRegionResult(region, peer, err) +func (c *codecPDClient) GetRegionByID(ctx context.Context, regionID uint64) (*pd.Region, error) { + region, err := c.Client.GetRegionByID(ctx, regionID) + return processRegionResult(region, err) } func (c *codecPDClient) ScanRegions(ctx context.Context, startKey []byte, endKey []byte, limit int) ([]*metapb.Region, []*metapb.Peer, error) { @@ -68,18 +68,18 @@ func (c *codecPDClient) ScanRegions(ctx context.Context, startKey []byte, endKey return regions, peers, nil } -func processRegionResult(region *metapb.Region, peer *metapb.Peer, err error) (*metapb.Region, *metapb.Peer, error) { +func processRegionResult(region *pd.Region, err error) (*pd.Region, error) { if err != nil { - return nil, nil, errors.Trace(err) + return nil, errors.Trace(err) } - if region == nil { - return nil, nil, nil + if region == nil || region.Meta == nil { + return nil, nil } - err = decodeRegionMetaKeyInPlace(region) + err = decodeRegionMetaKeyInPlace(region.Meta) if err != nil { - return nil, nil, errors.Trace(err) + return nil, errors.Trace(err) } - return region, peer, nil + return region, nil } func decodeRegionMetaKeyInPlace(r *metapb.Region) error { diff --git a/store/tikv/region_cache.go b/store/tikv/region_cache.go index 8a6d1704e807c..edf875db735e4 100644 --- a/store/tikv/region_cache.go +++ b/store/tikv/region_cache.go @@ -831,6 +831,29 @@ func (c *RegionCache) getRegionByIDFromCache(regionID uint64) *Region { return newestRegion } +func filterUnavailablePeers(region *pd.Region) { + if len(region.DownPeers) == 0 { + return + } + new := region.Meta.Peers[:0] + for _, p := range region.Meta.Peers { + available := true + for _, downPeer := range region.DownPeers { + if p.Id == downPeer.Id && p.StoreId == downPeer.StoreId { + available = false + break + } + } + if available { + new = append(new, p) + } + } + for i := len(new); i < len(region.Meta.Peers); i++ { + region.Meta.Peers[i] = nil + } + region.Meta.Peers = new +} + // loadRegion loads region from pd client, and picks the first peer as leader. // If the given key is the end key of the region that you want, you may set the second argument to true. This is useful // when processing in reverse order. @@ -844,13 +867,12 @@ func (c *RegionCache) loadRegion(bo *Backoffer, key []byte, isEndKey bool) (*Reg return nil, errors.Trace(err) } } - var meta *metapb.Region - var leader *metapb.Peer + var reg *pd.Region var err error if searchPrev { - meta, leader, err = c.pdClient.GetPrevRegion(bo.ctx, key) + reg, err = c.pdClient.GetPrevRegion(bo.ctx, key) } else { - meta, leader, err = c.pdClient.GetRegion(bo.ctx, key) + reg, err = c.pdClient.GetRegion(bo.ctx, key) } if err != nil { tikvRegionCacheCounterWithGetRegionError.Inc() @@ -861,21 +883,22 @@ func (c *RegionCache) loadRegion(bo *Backoffer, key []byte, isEndKey bool) (*Reg backoffErr = errors.Errorf("loadRegion from PD failed, key: %q, err: %v", key, err) continue } - if meta == nil { + if reg == nil || reg.Meta == nil { backoffErr = errors.Errorf("region not found for key %q", key) continue } - if len(meta.Peers) == 0 { - return nil, errors.New("receive Region with no peer") + filterUnavailablePeers(reg) + if len(reg.Meta.Peers) == 0 { + return nil, errors.New("receive Region with no available peer") } - if isEndKey && !searchPrev && bytes.Equal(meta.StartKey, key) && len(meta.StartKey) != 0 { + if isEndKey && !searchPrev && bytes.Equal(reg.Meta.StartKey, key) && len(reg.Meta.StartKey) != 0 { searchPrev = true continue } - region := &Region{meta: meta} + region := &Region{meta: reg.Meta} region.init(c) - if leader != nil { - c.switchToPeer(region, leader.StoreId) + if reg.Leader != nil { + c.switchToPeer(region, reg.Leader.StoreId) } return region, nil } @@ -891,7 +914,7 @@ func (c *RegionCache) loadRegionByID(bo *Backoffer, regionID uint64) (*Region, e return nil, errors.Trace(err) } } - meta, leader, err := c.pdClient.GetRegionByID(bo.ctx, regionID) + reg, err := c.pdClient.GetRegionByID(bo.ctx, regionID) if err != nil { tikvRegionCacheCounterWithGetRegionByIDError.Inc() } else { @@ -901,16 +924,17 @@ func (c *RegionCache) loadRegionByID(bo *Backoffer, regionID uint64) (*Region, e backoffErr = errors.Errorf("loadRegion from PD failed, regionID: %v, err: %v", regionID, err) continue } - if meta == nil { + if reg == nil || reg.Meta == nil { return nil, errors.Errorf("region not found for regionID %d", regionID) } - if len(meta.Peers) == 0 { - return nil, errors.New("receive Region with no peer") + filterUnavailablePeers(reg) + if len(reg.Meta.Peers) == 0 { + return nil, errors.New("receive Region with no available peer") } - region := &Region{meta: meta} + region := &Region{meta: reg.Meta} region.init(c) - if leader != nil { - c.switchToPeer(region, leader.GetStoreId()) + if reg.Leader != nil { + c.switchToPeer(region, reg.Leader.GetStoreId()) } return region, nil } diff --git a/store/tikv/store_test.go b/store/tikv/store_test.go index 30a0745ebd2c7..dd9f1fc932b92 100644 --- a/store/tikv/store_test.go +++ b/store/tikv/store_test.go @@ -139,32 +139,32 @@ func (c *mockPDClient) GetTSAsync(ctx context.Context) pd.TSFuture { return nil } -func (c *mockPDClient) GetRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) { +func (c *mockPDClient) GetRegion(ctx context.Context, key []byte) (*pd.Region, error) { c.RLock() defer c.RUnlock() if c.stop { - return nil, nil, errors.Trace(errStopped) + return nil, errors.Trace(errStopped) } return c.client.GetRegion(ctx, key) } -func (c *mockPDClient) GetPrevRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) { +func (c *mockPDClient) GetPrevRegion(ctx context.Context, key []byte) (*pd.Region, error) { c.RLock() defer c.RUnlock() if c.stop { - return nil, nil, errors.Trace(errStopped) + return nil, errors.Trace(errStopped) } return c.client.GetPrevRegion(ctx, key) } -func (c *mockPDClient) GetRegionByID(ctx context.Context, regionID uint64) (*metapb.Region, *metapb.Peer, error) { +func (c *mockPDClient) GetRegionByID(ctx context.Context, regionID uint64) (*pd.Region, error) { c.RLock() defer c.RUnlock() if c.stop { - return nil, nil, errors.Trace(errStopped) + return nil, errors.Trace(errStopped) } return c.client.GetRegionByID(ctx, regionID) } From 134e691d6f5f5b6ba5d87ae4643488d90e389a1b Mon Sep 17 00:00:00 2001 From: Kenan Yao Date: Thu, 21 May 2020 23:47:55 +0800 Subject: [PATCH 36/74] planner: check required order property for enforced stream aggregation (#17338) --- planner/core/exhaust_physical_plans.go | 4 +- planner/core/integration_test.go | 26 ++++++++ .../core/testdata/integration_suite_in.json | 9 +++ .../core/testdata/integration_suite_out.json | 60 +++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 57aaad642af17..e7b79118fbca4 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -1599,7 +1599,9 @@ func (la *LogicalAggregation) getEnforcedStreamAggs(prop *property.PhysicalPrope Enforced: true, Items: property.ItemsFromCols(la.groupByCols, desc), } - + if !prop.IsPrefix(childProp) { + return enforcedAggs + } taskTypes := []property.TaskType{property.CopSingleReadTaskType, property.CopDoubleReadTaskType} if la.HasDistinct() { // TODO: remove AllowDistinctAggPushDown after the cost estimation of distinct pushdown is implemented. diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index 0349a2aabef7f..0fa2195672046 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -985,3 +985,29 @@ func (s *testIntegrationSerialSuite) TestIssue16837(c *C) { tk.MustExec("insert into t values (2, 1, 1, 1, 2)") tk.MustQuery("select /*+ use_index_merge(t,c,idx_ab) */ * from t where a = 1 or (e = 1 and c = 1)").Check(testkit.Rows()) } + +func (s *testIntegrationSuite) TestStreamAggProp(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)") + tk.MustExec("insert into t values(1),(1),(2)") + + var input []string + var output []struct { + SQL string + Plan []string + Res []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()) + output[i].Res = s.testData.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Res...)) + } +} diff --git a/planner/core/testdata/integration_suite_in.json b/planner/core/testdata/integration_suite_in.json index 360b6bb3d9ad2..3c691d4ee1fd8 100644 --- a/planner/core/testdata/integration_suite_in.json +++ b/planner/core/testdata/integration_suite_in.json @@ -127,5 +127,14 @@ "explain select * from t where a in (14, floor(3.47))", "explain select * from t where b in (3, 4)" ] + }, + { + "name": "TestStreamAggProp", + "cases": [ + "select /*+ stream_agg() */ count(*) c from t group by a order by c limit 1", + "select /*+ stream_agg() */ count(*) c from t group by a order by c", + "select /*+ stream_agg() */ count(*) c from t group by a order by a limit 1", + "select /*+ stream_agg() */ count(*) c from t group by a order by a" + ] } ] diff --git a/planner/core/testdata/integration_suite_out.json b/planner/core/testdata/integration_suite_out.json index 7418675766b40..a0c2f09bb125e 100644 --- a/planner/core/testdata/integration_suite_out.json +++ b/planner/core/testdata/integration_suite_out.json @@ -582,5 +582,65 @@ ] } ] + }, + { + "Name": "TestStreamAggProp", + "Cases": [ + { + "SQL": "select /*+ stream_agg() */ count(*) c from t group by a order by c limit 1", + "Plan": [ + "TopN_10 1.00 root Column#3, offset:0, count:1", + "└─StreamAgg_17 8000.00 root group by:test.t.a, funcs:count(1)->Column#3", + " └─Sort_22 10000.00 root test.t.a", + " └─TableReader_21 10000.00 root data:TableFullScan_20", + " └─TableFullScan_20 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Res": [ + "1" + ] + }, + { + "SQL": "select /*+ stream_agg() */ count(*) c from t group by a order by c", + "Plan": [ + "Sort_5 8000.00 root Column#3", + "└─StreamAgg_11 8000.00 root group by:test.t.a, funcs:count(1)->Column#3", + " └─Sort_16 10000.00 root test.t.a", + " └─TableReader_15 10000.00 root data:TableFullScan_14", + " └─TableFullScan_14 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Res": [ + "1", + "2" + ] + }, + { + "SQL": "select /*+ stream_agg() */ count(*) c from t group by a order by a limit 1", + "Plan": [ + "Projection_8 1.00 root Column#3", + "└─Limit_14 1.00 root offset:0, count:1", + " └─StreamAgg_27 1.00 root group by:test.t.a, funcs:count(1)->Column#3, funcs:firstrow(test.t.a)->test.t.a", + " └─Sort_32 1.25 root test.t.a", + " └─TableReader_31 1.25 root data:TableFullScan_30", + " └─TableFullScan_30 1.25 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Res": [ + "2" + ] + }, + { + "SQL": "select /*+ stream_agg() */ count(*) c from t group by a order by a", + "Plan": [ + "Projection_6 8000.00 root Column#3", + "└─StreamAgg_21 8000.00 root group by:test.t.a, funcs:count(1)->Column#3, funcs:firstrow(test.t.a)->test.t.a", + " └─Sort_17 10000.00 root test.t.a", + " └─TableReader_16 10000.00 root data:TableFullScan_15", + " └─TableFullScan_15 10000.00 cop[tikv] table:t keep order:false, stats:pseudo" + ], + "Res": [ + "2", + "1" + ] + } + ] } ] From c59cddcc01e46adf4520d161f10a8e19bb58b785 Mon Sep 17 00:00:00 2001 From: Lonng Date: Fri, 22 May 2020 11:03:55 +0800 Subject: [PATCH 37/74] refine the standardize-error-codes-and-messages RFC (#17352) --- ...08-standardize-error-codes-and-messages.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/design/2020-05-08-standardize-error-codes-and-messages.md b/docs/design/2020-05-08-standardize-error-codes-and-messages.md index 8aae92d441c83..40cc8cd038484 100644 --- a/docs/design/2020-05-08-standardize-error-codes-and-messages.md +++ b/docs/design/2020-05-08-standardize-error-codes-and-messages.md @@ -24,15 +24,15 @@ keep a metafile in the code repository. The metafile should be a toml file which ```toml [error.8005] -error = '''Write Conflict, txnStartTS is stale''' -message = '''Transactions in TiDB encounter write conflicts.''' +message = '''Write Conflict, txnStartTS is stale''' +description = '''Transactions in TiDB encounter write conflicts.''' workaround = ''' Check whether `tidb_disable_txn_auto_retry` is set to `on`. If so, set it to `off`; if it is already `off`, increase the value of `tidb_retry_limit` until the error no longer occurs. ''' [error.9005] -error = '''Region is unavailable''' -message = ''' +message = '''Region is unavailable''' +description = ''' A certain Raft Group is not available, such as the number of replicas is not enough. This error usually occurs when the TiKV server is busy or the TiKV node is down. ''' @@ -60,14 +60,14 @@ The json format of metafile is like: [ { "code": 8005, - "error": "Write Conflict, txnStartTS is stale", - "message": "Transactions in TiDB encounter write conflicts.", + "message": "Write Conflict, txnStartTS is stale", + "description": "Transactions in TiDB encounter write conflicts.", "workaround": "Check whether `tidb_disable_txn_auto_retry` is set to `on`. If so, set it to `off`; if it is already `off`, increase the value of `tidb_retry_limit` until the error no longer occurs." }, { "code": 9005, - "error": "Region is unavailable", - "message": "A certain Raft Group is not available, such as the number of replicas is not enough.\nThis error usually occurs when the TiKV server is busy or the TiKV node is down.", + "message": "Region is unavailable", + "description": "A certain Raft Group is not available, such as the number of replicas is not enough.\nThis error usually occurs when the TiKV server is busy or the TiKV node is down.", "workaround": "Check the status, monitoring data and log of the TiKV server." } ] @@ -136,8 +136,8 @@ As the syntax above, the 9005 block is the message part of 8005 block, so we exp ```toml [error.8005] -error = '''Write Conflict, txnStartTS is stale''' -message = '''Transactions in TiDB encounter write conflicts.''' +message = '''Write Conflict, txnStartTS is stale''' +description = '''Transactions in TiDB encounter write conflicts.''' workaround = ''' ## Code: 9005 ### Error @@ -170,8 +170,8 @@ In the discussion above, an error has at least 4 parts: Besides, we can append a optional tags field to it: ```toml [error.9005] -error = "" message = "" +description = "" workaround = "" tags = ["tikv"] ``` @@ -205,4 +205,4 @@ curl https://raw.githubusercontent.com/tikv/pd/master/errors.toml -o errors/pd.t Then there are two tasks will be execute on the errors directory: - Build a program that embed these errors, the program is used to quickly access information corresponding to error codes. -- Build the [error code document](https://pingcap.com/docs/stable/reference/error-codes/) from these errors. \ No newline at end of file +- Build the [error code document](https://pingcap.com/docs/stable/reference/error-codes/) from these errors. From 38298c2e3e8ae6438b2b9caa696586087ea58a4d Mon Sep 17 00:00:00 2001 From: "Zhuomin(Charming) Liu" Date: Fri, 22 May 2020 17:40:10 +0800 Subject: [PATCH 38/74] planner: fix wrong index merge join plan for join key is not the prefix of index (#16894) --- executor/index_lookup_join_test.go | 14 ++++++++++++++ planner/core/exhaust_physical_plans.go | 8 ++++++++ planner/core/testdata/integration_suite_out.json | 4 ++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/executor/index_lookup_join_test.go b/executor/index_lookup_join_test.go index 70a0c9fffe777..f67dfcdd8eb84 100644 --- a/executor/index_lookup_join_test.go +++ b/executor/index_lookup_join_test.go @@ -197,3 +197,17 @@ func (s *testSuite5) TestIndexJoinMultiCondition(c *C) { tk.MustExec("insert into t2 values (0,1), (0,2), (0,3)") tk.MustQuery("select /*+ TIDB_INLJ(t1) */ count(*) from t1, t2 where t1.a = t2.a and t1.b < t2.b").Check(testkit.Rows("3")) } + +func (s *testSuite5) TestIssue16887(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists admin_roles, admin_role_has_permissions") + tk.MustExec("CREATE TABLE `admin_role_has_permissions` (`permission_id` bigint(20) unsigned NOT NULL, `role_id` bigint(20) unsigned NOT NULL, PRIMARY KEY (`permission_id`,`role_id`), KEY `admin_role_has_permissions_role_id_foreign` (`role_id`))") + tk.MustExec("CREATE TABLE `admin_roles` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '角色名称', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`))") + tk.MustExec("INSERT INTO `admin_roles` (`id`, `name`, `created_at`, `updated_at`) VALUES(1, 'admin','2020-04-27 02:40:03', '2020-04-27 02:40:03'),(2, 'developer','2020-04-27 02:40:03', '2020-04-27 02:40:03'),(3, 'analyst','2020-04-27 02:40:03', '2020-04-27 02:40:03'),(4, 'channel_admin','2020-04-27 02:40:03', '2020-04-27 02:40:03'),(5, 'test','2020-04-27 02:40:08', '2020-04-27 02:40:08')") + tk.MustExec("INSERT INTO `admin_role_has_permissions` (`permission_id`, `role_id`) VALUES(1, 1),(2, 1),(3, 1),(4, 1),(5, 1),(6, 1),(7, 1),(8, 1),(9, 1),(10, 1),(11, 1),(12, 1),(13, 1),(14, 1),(15, 1),(16, 1),(17, 1),(18, 1),(19, 1),(20, 1),(21, 1),(22, 1),(23, 1),(24, 1),(25, 1),(26, 1),(27, 1),(28, 1),(29, 1),(30, 1),(31, 1),(32, 1),(33, 1),(34, 1),(35, 1),(36, 1),(37, 1),(38, 1),(39, 1),(40, 1),(41, 1),(42, 1),(43, 1),(44, 1),(45, 1),(46, 1),(47, 1),(48, 1),(49, 1),(50, 1),(51, 1),(52, 1),(53, 1),(54, 1),(55, 1),(56, 1),(57, 1),(58, 1),(59, 1),(60, 1),(61, 1),(62, 1),(63, 1),(64, 1),(65, 1),(66, 1),(67, 1),(68, 1),(69, 1),(70, 1),(71, 1),(72, 1),(73, 1),(74, 1),(75, 1),(76, 1),(77, 1),(78, 1),(79, 1),(80, 1),(81, 1),(82, 1),(83, 1),(5, 4),(6, 4),(7, 4),(84, 5),(85, 5),(86, 5)") + rows := tk.MustQuery("SELECT /*+ inl_merge_join(admin_role_has_permissions) */ `admin_roles`.* FROM `admin_roles` INNER JOIN `admin_role_has_permissions` ON `admin_roles`.`id` = `admin_role_has_permissions`.`role_id` WHERE `admin_role_has_permissions`.`permission_id`\n IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67)").Rows() + c.Assert(len(rows), Equals, 70) + rows = tk.MustQuery("show warnings").Rows() + c.Assert(len(rows) > 0, Equals, true) +} diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index e7b79118fbca4..8ead7e3844164 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -448,9 +448,17 @@ func (p *LogicalJoin) constructIndexMergeJoin( keyOffMap[idxOff] = i } sort.Slice(keyOffMapList, func(i, j int) bool { return keyOffMapList[i] < keyOffMapList[j] }) + keyIsIndexPrefix := true for keyOff, idxOff := range keyOffMapList { + if keyOff != idxOff { + keyIsIndexPrefix = false + break + } keyOff2KeyOffOrderByIdx[keyOffMap[idxOff]] = keyOff } + if !keyIsIndexPrefix { + continue + } // isOuterKeysPrefix means whether the outer join keys are the prefix of the prop items. isOuterKeysPrefix := len(join.OuterJoinKeys) <= len(prop.Items) compareFuncs := make([]expression.CompareFunc, 0, len(join.OuterJoinKeys)) diff --git a/planner/core/testdata/integration_suite_out.json b/planner/core/testdata/integration_suite_out.json index a0c2f09bb125e..85dc39cf097d8 100644 --- a/planner/core/testdata/integration_suite_out.json +++ b/planner/core/testdata/integration_suite_out.json @@ -263,8 +263,8 @@ "SQL": "desc select /*+ TIDB_INLJ(t2)*/ * from t1, t2 where t1.a = t2.a and t1.b = t2.a and t1.b = t2.b", "Plan": [ "IndexJoin_12 12487.50 root inner join, inner:TableReader_11, outer key:test.t1.a, test.t1.b, inner key:test.t2.a, test.t2.a, other cond:eq(test.t1.b, test.t2.b)", - "├─IndexReader_21(Build) 9990.00 root index:IndexFullScan_20", - "│ └─IndexFullScan_20 9990.00 cop[tikv] table:t1, index:idx_t1_b(b) keep order:false, stats:pseudo", + "├─IndexReader_20(Build) 9990.00 root index:IndexFullScan_19", + "│ └─IndexFullScan_19 9990.00 cop[tikv] table:t1, index:idx_t1_b(b) keep order:false, stats:pseudo", "└─TableReader_11(Probe) 1.00 root data:Selection_10", " └─Selection_10 1.00 cop[tikv] not(isnull(test.t2.b))", " └─TableRangeScan_9 1.00 cop[tikv] table:t2 range: decided by [test.t1.a test.t1.b], keep order:false, stats:pseudo" From 5d015904323657552db0c6da9cb7a72daf994da0 Mon Sep 17 00:00:00 2001 From: lysu Date: Mon, 25 May 2020 10:38:28 +0800 Subject: [PATCH 39/74] tikv: fix panic after sending fail with ambiguous errors (#17211) --- store/tikv/client_batch.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/store/tikv/client_batch.go b/store/tikv/client_batch.go index 08520afc51219..efa5237c8511c 100644 --- a/store/tikv/client_batch.go +++ b/store/tikv/client_batch.go @@ -232,6 +232,7 @@ func (c *batchCommandsClient) send(request *tikvpb.BatchCommandsRequest, entries logutil.BgLogger().Info( "sending batch commands meets error", zap.String("target", c.target), + zap.Uint64s("requestIDs", request.RequestIds), zap.Error(err), ) c.failPendingRequests(err) @@ -345,10 +346,10 @@ func (c *batchCommandsClient) batchRecvLoop(cfg config.TiKVClient, tikvTransport for i, requestID := range resp.GetRequestIds() { value, ok := c.batched.Load(requestID) if !ok { - // There shouldn't be any unknown responses because if the old entries - // are cleaned by `failPendingRequests`, the stream must be re-created - // so that old responses will be never received. - panic("batchRecvLoop receives a unknown response") + // this maybe caused by batchCommandsClient#send meets ambiguous error that request has be sent to TiKV but still report a error. + // then TiKV will send response back though stream and reach here. + logutil.BgLogger().Warn("batchRecvLoop receives outdated response", zap.Uint64("requestID", requestID)) + continue } entry := value.(*batchCommandsEntry) logutil.Eventf(entry.ctx, "receive %T response with other %d batched requests from %s", responses[i].GetCmd(), len(responses), c.target) From 43965127c69f7ecb1821e622ecfdaa3de4f334f1 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Mon, 25 May 2020 13:32:22 +0800 Subject: [PATCH 40/74] go.mod: update parser version to support delete partition (p0,p1) (#17381) --- executor/executor_test.go | 20 ++++++++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/executor/executor_test.go b/executor/executor_test.go index d37473cae3b2b..8bc7e134400fa 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -3892,6 +3892,26 @@ func (s *testSuiteP1) TestSelectPartition(c *C) { tk.MustQuery("select a, b from th where b>10").Check(testkit.Rows("11 11")) } +func (s *testSuiteP1) TestDeletePartition(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists t1`) + tk.MustExec(`create table t1 (a int) partition by range (a) ( + partition p0 values less than (10), + partition p1 values less than (20), + partition p2 values less than (30), + partition p3 values less than (40), + partition p4 values less than MAXVALUE + )`) + tk.MustExec("insert into t1 values (1),(11),(21),(31)") + tk.MustExec("delete from t1 partition (p4)") + tk.MustQuery("select * from t1 order by a").Check(testkit.Rows("1", "11", "21", "31")) + tk.MustExec("delete from t1 partition (p0) where a > 10") + tk.MustQuery("select * from t1 order by a").Check(testkit.Rows("1", "11", "21", "31")) + tk.MustExec("delete from t1 partition (p0,p1,p2)") + tk.MustQuery("select * from t1").Check(testkit.Rows("31")) +} + func (s *testSuite) TestSelectView(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/go.mod b/go.mod index a412bfea2a61c..4dac0fa4186b1 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/pingcap/goleveldb v0.0.0-20191226122134-f82aafb29989 github.com/pingcap/kvproto v0.0.0-20200518112156-d4aeb467de29 github.com/pingcap/log v0.0.0-20200511115504-543df19646ad - github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1 + github.com/pingcap/parser v0.0.0-20200522094936-3b720a0512a6 github.com/pingcap/pd/v4 v4.0.0-rc.2.0.20200520083007-2c251bd8f181 github.com/pingcap/sysutil v0.0.0-20200408114249-ed3bd6f7fdb1 github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200514040632-f76b3e428e19+incompatible diff --git a/go.sum b/go.sum index d1f27831724ae..ddda86ec3d3c4 100644 --- a/go.sum +++ b/go.sum @@ -448,8 +448,8 @@ github.com/pingcap/log v0.0.0-20200511115504-543df19646ad/go.mod h1:4rbK1p9ILyIf github.com/pingcap/parser v0.0.0-20200424075042-8222d8b724a4/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657 h1:2ceTso30kmgMeddZ4iZ6zrK8N9eFF8zmCa1hSSE1tXc= github.com/pingcap/parser v0.0.0-20200507022230-f3bf29096657/go.mod h1:9v0Edh8IbgjGYW2ArJr19E+bvL8zKahsFp+ixWeId+4= -github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1 h1:xPCqyGxvHfAZrBnfr4Dbm7RZFCL2QIKz66UyJYYbrOM= -github.com/pingcap/parser v0.0.0-20200518090819-ec1e13b948b1/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= +github.com/pingcap/parser v0.0.0-20200522094936-3b720a0512a6 h1:sa3NjjIEU9JOY/P8bhn+SJupcKZ497b3sfPVfvyCm2A= +github.com/pingcap/parser v0.0.0-20200522094936-3b720a0512a6/go.mod h1:vQdbJqobJAgFyiRNNtXahpMoGWwPEuWciVEK5A20NS0= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2 h1:JTzYYukREvxVSKW/ncrzNjFitd8snoQ/Xz32pw8i+s8= github.com/pingcap/pd/v4 v4.0.0-rc.1.0.20200422143320-428acd53eba2/go.mod h1:s+utZtXDznOiL24VK0qGmtoHjjXNsscJx3m1n8cC56s= github.com/pingcap/pd/v4 v4.0.0-rc.2.0.20200520083007-2c251bd8f181 h1:FM+PzdoR3fmWAJx3ug+p5aOgs5aZYwFkoDL7Potdsz0= From 2bdf6a01cc7bd3d8e95dee6fe9f42507ce46e5c7 Mon Sep 17 00:00:00 2001 From: crazycs Date: Mon, 25 May 2020 21:25:33 +0800 Subject: [PATCH 41/74] util/admin: support admin check index on partition table (#17183) --- executor/admin_test.go | 26 +++++++++ executor/builder.go | 23 +------- executor/executor.go | 63 ++------------------- planner/core/common_plans.go | 10 +--- planner/core/planbuilder.go | 105 +++++++++++++++++++---------------- 5 files changed, 92 insertions(+), 135 deletions(-) diff --git a/executor/admin_test.go b/executor/admin_test.go index 89a726d737884..fe5a40c6e9368 100644 --- a/executor/admin_test.go +++ b/executor/admin_test.go @@ -47,6 +47,32 @@ func (s *testSuite1) TestAdminCheckIndexRange(c *C) { result.Check(testkit.Rows("-1 hi 4", "2 cd 2")) } +func (s *testSuite5) TestAdminCheckIndex(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + check := func() { + tk.MustExec("insert admin_test (c1, c2) values (1, 1), (2, 2), (5, 5), (10, 10), (11, 11), (NULL, NULL)") + tk.MustExec("admin check index admin_test c1") + tk.MustExec("admin check index admin_test c2") + } + tk.MustExec("drop table if exists admin_test") + tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, index (c1), unique key(c2))") + check() + + // Test for hash partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, index (c1), unique key(c2)) partition by hash(c2) partitions 5;") + check() + + // Test for range partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec(`create table admin_test (c1 int, c2 int, c3 int default 1, index (c1), unique key(c2)) PARTITION BY RANGE ( c2 ) ( + PARTITION p0 VALUES LESS THAN (5), + PARTITION p1 VALUES LESS THAN (10), + PARTITION p2 VALUES LESS THAN (MAXVALUE))`) + check() +} + func (s *testSuite5) TestAdminRecoverIndex(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/executor/builder.go b/executor/builder.go index 438f868b2d13c..e92df802d6acd 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -107,8 +107,6 @@ func (b *executorBuilder) build(p plannercore.Plan) Executor { return b.buildChange(v) case *plannercore.CheckTable: return b.buildCheckTable(v) - case *plannercore.CheckIndex: - return b.buildCheckIndex(v) case *plannercore.RecoverIndex: return b.buildRecoverIndex(v) case *plannercore.CleanupIndex: @@ -332,26 +330,6 @@ func (b *executorBuilder) buildShowSlow(v *plannercore.ShowSlow) Executor { return e } -func (b *executorBuilder) buildCheckIndex(v *plannercore.CheckIndex) Executor { - readerExec, err := buildNoRangeIndexLookUpReader(b, v.IndexLookUpReader) - if err != nil { - b.err = err - return nil - } - - buildIndexLookUpChecker(b, v.IndexLookUpReader, readerExec) - - e := &CheckIndexExec{ - baseExecutor: newBaseExecutor(b.ctx, v.Schema(), v.ExplainID()), - dbName: v.DBName, - tableName: readerExec.table.Meta().Name.L, - idxName: v.IdxName, - is: b.is, - src: readerExec, - } - return e -} - // buildIndexLookUpChecker builds check information to IndexLookUpReader. func buildIndexLookUpChecker(b *executorBuilder, readerPlan *plannercore.PhysicalIndexLookUpReader, readerExec *IndexLookUpExecutor) { @@ -404,6 +382,7 @@ func (b *executorBuilder) buildCheckTable(v *plannercore.CheckTable) Executor { srcs: readerExecs, exitCh: make(chan struct{}), retCh: make(chan error, len(readerExecs)), + checkIndex: v.CheckIndex, } return e } diff --git a/executor/executor.go b/executor/executor.go index 230035a9fec7c..ad310114b3702 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -62,7 +62,6 @@ import ( var ( _ Executor = &baseExecutor{} - _ Executor = &CheckIndexExec{} _ Executor = &CheckTableExec{} _ Executor = &HashAggExec{} _ Executor = &HashJoinExec{} @@ -637,6 +636,7 @@ type CheckTableExec struct { is infoschema.InfoSchema exitCh chan struct{} retCh chan error + checkIndex bool } // Open implements the Executor Open interface. @@ -724,6 +724,10 @@ func (e *CheckTableExec) Next(ctx context.Context, req *chunk.Chunk) error { } greater, idxOffset, err := admin.CheckIndicesCount(e.ctx, e.dbName, e.table.Meta().Name.O, idxNames) if err != nil { + // For admin check index statement, for speed up and compatibility, doesn't do below checks. + if e.checkIndex { + return errors.Trace(err) + } if greater == admin.IdxCntGreater { err = e.checkTableIndexHandle(ctx, e.indexInfos[idxOffset]) } else if greater == admin.TblCntGreater { @@ -746,7 +750,7 @@ func (e *CheckTableExec) Next(ctx context.Context, req *chunk.Chunk) error { util.WithRecovery(func() { err1 := e.checkIndexHandle(ctx, e.srcs[num]) if err1 != nil { - logutil.Logger(ctx).Info("check index handle failed", zap.Error(err)) + logutil.Logger(ctx).Info("check index handle failed", zap.Error(err1)) } }, e.handlePanic) }(i) @@ -790,61 +794,6 @@ func (e *CheckTableExec) checkTableRecord(idxOffset int) error { return nil } -// CheckIndexExec represents the executor of checking an index. -// It is built from the "admin check index" statement, and it checks -// the consistency of the index data with the records of the table. -type CheckIndexExec struct { - baseExecutor - - dbName string - tableName string - idxName string - src *IndexLookUpExecutor - done bool - is infoschema.InfoSchema -} - -// Open implements the Executor Open interface. -func (e *CheckIndexExec) Open(ctx context.Context) error { - if err := e.baseExecutor.Open(ctx); err != nil { - return err - } - if err := e.src.Open(ctx); err != nil { - return err - } - e.done = false - return nil -} - -// Close implements the Executor Close interface. -func (e *CheckIndexExec) Close() error { - return e.src.Close() -} - -// Next implements the Executor Next interface. -func (e *CheckIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { - if e.done { - return nil - } - defer func() { e.done = true }() - - _, _, err := admin.CheckIndicesCount(e.ctx, e.dbName, e.tableName, []string{e.idxName}) - if err != nil { - return err - } - chk := newFirstChunk(e.src) - for { - err := Next(ctx, e.src, chk) - if err != nil { - return err - } - if chk.NumRows() == 0 { - break - } - } - return nil -} - // ShowSlowExec represents the executor of showing the slow queries. // It is build from the "admin show slow" statement: // admin show slow top [internal | all] N diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index d06288ba66eed..411e56235e1d6 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -80,6 +80,7 @@ type CheckTable struct { Table table.Table IndexInfos []*model.IndexInfo IndexLookUpReaders []*PhysicalIndexLookUpReader + CheckIndex bool } // RecoverIndex is used for backfilling corrupted index data. @@ -98,15 +99,6 @@ type CleanupIndex struct { IndexName string } -// CheckIndex is used for checking index data, built from the 'admin check index' statement. -type CheckIndex struct { - baseSchemaProducer - - IndexLookUpReader *PhysicalIndexLookUpReader - DBName string - IdxName string -} - // CheckIndexRange is used for checking index data, output the index values that handle within begin and end. type CheckIndexRange struct { baseSchemaProducer diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index ae6fb6bb18628..00fa69f4115e7 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -885,53 +885,15 @@ func (b *PlanBuilder) buildPrepare(x *ast.PrepareStmt) Plan { return p } -func (b *PlanBuilder) buildCheckIndex(ctx context.Context, dbName model.CIStr, as *ast.AdminStmt) (Plan, error) { - tblName := as.Tables[0] - tbl, err := b.is.TableByName(dbName, tblName.Name) - if err != nil { - return nil, err - } - tblInfo := tbl.Meta() - - // get index information - var idx *model.IndexInfo - for _, index := range tblInfo.Indices { - if index.Name.L == strings.ToLower(as.Index) { - idx = index - break - } - } - if idx == nil { - return nil, errors.Errorf("index %s do not exist", as.Index) - } - if idx.State != model.StatePublic { - return nil, errors.Errorf("index %s state %s isn't public", as.Index, idx.State) - } - - return b.buildPhysicalIndexLookUpReader(ctx, dbName, tbl, idx) -} - func (b *PlanBuilder) buildAdmin(ctx context.Context, as *ast.AdminStmt) (Plan, error) { var ret Plan var err error switch as.Tp { - case ast.AdminCheckTable: + case ast.AdminCheckTable, ast.AdminCheckIndex: ret, err = b.buildAdminCheckTable(ctx, as) if err != nil { return ret, err } - case ast.AdminCheckIndex: - dbName := as.Tables[0].Schema - readerPlan, err := b.buildCheckIndex(ctx, dbName, as) - if err != nil { - return ret, err - } - - ret = &CheckIndex{ - DBName: dbName.L, - IdxName: as.Index, - IndexLookUpReader: readerPlan.(*PhysicalIndexLookUpReader), - } case ast.AdminRecoverIndex: p := &RecoverIndex{Table: as.Tables[0], IndexName: as.Index} p.setSchemaAndNames(buildRecoverIndexFields()) @@ -1168,12 +1130,12 @@ func (b *PlanBuilder) buildPhysicalIndexLookUpReader(ctx context.Context, dbName return rootT.p, nil } -func (b *PlanBuilder) buildPhysicalIndexLookUpReaders(ctx context.Context, dbName model.CIStr, tbl table.Table) ([]Plan, []*model.IndexInfo, error) { +func (b *PlanBuilder) buildPhysicalIndexLookUpReaders(ctx context.Context, dbName model.CIStr, tbl table.Table, indices []table.Index) ([]Plan, []*model.IndexInfo, error) { tblInfo := tbl.Meta() // get index information indexInfos := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) indexLookUpReaders := make([]Plan, 0, len(tblInfo.Indices)) - for _, idx := range tbl.Indices() { + for _, idx := range indices { idxInfo := idx.Meta() if idxInfo.State != model.StatePublic { logutil.Logger(context.Background()).Info("build physical index lookup reader, the index isn't public", @@ -1207,17 +1169,40 @@ func (b *PlanBuilder) buildPhysicalIndexLookUpReaders(ctx context.Context, dbNam } func (b *PlanBuilder) buildAdminCheckTable(ctx context.Context, as *ast.AdminStmt) (*CheckTable, error) { - tbl := as.Tables[0] + tblName := as.Tables[0] tableInfo := as.Tables[0].TableInfo - table, ok := b.is.TableByID(tableInfo.ID) + tbl, ok := b.is.TableByID(tableInfo.ID) if !ok { - return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(tbl.DBInfo.Name.O, tableInfo.Name.O) + return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(tblName.DBInfo.Name.O, tableInfo.Name.O) } p := &CheckTable{ - DBName: tbl.Schema.O, - Table: table, + DBName: tblName.Schema.O, + Table: tbl, + } + var readerPlans []Plan + var indexInfos []*model.IndexInfo + var err error + if as.Tp == ast.AdminCheckIndex { + // get index information + var idx table.Index + idxName := strings.ToLower(as.Index) + for _, index := range tbl.Indices() { + if index.Meta().Name.L == idxName { + idx = index + break + } + } + if idx == nil { + return nil, errors.Errorf("index %s do not exist", as.Index) + } + if idx.Meta().State != model.StatePublic { + return nil, errors.Errorf("index %s state %s isn't public", as.Index, idx.Meta().State) + } + p.CheckIndex = true + readerPlans, indexInfos, err = b.buildPhysicalIndexLookUpReaders(ctx, tblName.Schema, tbl, []table.Index{idx}) + } else { + readerPlans, indexInfos, err = b.buildPhysicalIndexLookUpReaders(ctx, tblName.Schema, tbl, tbl.Indices()) } - readerPlans, indexInfos, err := b.buildPhysicalIndexLookUpReaders(ctx, tbl.Schema, table) if err != nil { return nil, errors.Trace(err) } @@ -1230,6 +1215,32 @@ func (b *PlanBuilder) buildAdminCheckTable(ctx context.Context, as *ast.AdminStm return p, nil } +func (b *PlanBuilder) buildCheckIndex(ctx context.Context, dbName model.CIStr, as *ast.AdminStmt) (Plan, error) { + tblName := as.Tables[0] + tbl, err := b.is.TableByName(dbName, tblName.Name) + if err != nil { + return nil, err + } + tblInfo := tbl.Meta() + + // get index information + var idx *model.IndexInfo + for _, index := range tblInfo.Indices { + if index.Name.L == strings.ToLower(as.Index) { + idx = index + break + } + } + if idx == nil { + return nil, errors.Errorf("index %s do not exist", as.Index) + } + if idx.State != model.StatePublic { + return nil, errors.Errorf("index %s state %s isn't public", as.Index, idx.State) + } + + return b.buildPhysicalIndexLookUpReader(ctx, dbName, tbl, idx) +} + func (b *PlanBuilder) buildCheckIndexSchema(tn *ast.TableName, indexName string) (*expression.Schema, types.NameSlice, error) { schema := expression.NewSchema() var names types.NameSlice From 745e979a42625d819c65920a409300376d3290f2 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Tue, 26 May 2020 10:52:11 +0800 Subject: [PATCH 42/74] expression: fix data race when simpleRewriter rewrite ast.ColumnNameExpr concurrently (#17353) --- planner/core/partition_pruning_test.go | 4 ++-- planner/core/rule_partition_processor.go | 25 +++++++++++------------- table/tables/partition.go | 6 ------ 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/planner/core/partition_pruning_test.go b/planner/core/partition_pruning_test.go index 70ef8a5413f17..c5d4c4c191b8f 100644 --- a/planner/core/partition_pruning_test.go +++ b/planner/core/partition_pruning_test.go @@ -285,7 +285,7 @@ func (s *testPartitionPruningSuite) TestPartitionRangePrunner2VarChar(c *C) { lessThan[i] = tmp[0] } - prunner := &rangeColumnPruner{lessThan, tc.columns[0], true} + prunner := &rangeColumnsPruner{lessThan, tc.columns[0], true} cases := []struct { input string result partitionRangeOR @@ -333,7 +333,7 @@ func (s *testPartitionPruningSuite) TestPartitionRangePrunner2Date(c *C) { lessThan[i] = tmp[0] } - prunner := &rangeColumnPruner{lessThan, tc.columns[0], false} + prunner := &rangeColumnsPruner{lessThan, tc.columns[0], false} cases := []struct { input string result partitionRangeOR diff --git a/planner/core/rule_partition_processor.go b/planner/core/rule_partition_processor.go index 5997bb957cf4c..be3014ac0bafc 100644 --- a/planner/core/rule_partition_processor.go +++ b/planner/core/rule_partition_processor.go @@ -747,43 +747,40 @@ func (s *partitionProcessor) pruneRangeColumnsPartition(ds *DataSource, pi *mode return s.makeUnionAllChildren(ds, pi, result) } - pruner, err := makeRangeColumnPruner(ds, pe.ForRangeColumnsPruning) + pruner, err := makeRangeColumnPruner(ds, pi, pe.ForRangeColumnsPruning) if err == nil { result = partitionRangeForCNFExpr(ds.ctx, ds.allConds, pruner, result) } return s.makeUnionAllChildren(ds, pi, result) } -var _ partitionRangePruner = &rangeColumnPruner{} +var _ partitionRangePruner = &rangeColumnsPruner{} -// rangeColumnPruner is used by 'partition by range columns'. -type rangeColumnPruner struct { +// rangeColumnsPruner is used by 'partition by range columns'. +type rangeColumnsPruner struct { data []expression.Expression partCol *expression.Column maxvalue bool } -func makeRangeColumnPruner(ds *DataSource, from *tables.ForRangeColumnsPruning) (*rangeColumnPruner, error) { +func makeRangeColumnPruner(ds *DataSource, pi *model.PartitionInfo, from *tables.ForRangeColumnsPruning) (*rangeColumnsPruner, error) { schema := expression.NewSchema(ds.TblCols...) - expr, err := expression.RewriteSimpleExprWithNames(ds.ctx, from.Column, schema, ds.names) - if err != nil { - return nil, err - } - partCol := expr.(*expression.Column) + idx := expression.FindFieldNameIdxByColName(ds.names, pi.Columns[0].L) + partCol := schema.Columns[idx] data := make([]expression.Expression, len(from.LessThan)) for i := 0; i < len(from.LessThan); i++ { if from.LessThan[i] != nil { data[i] = from.LessThan[i].Clone() } } - return &rangeColumnPruner{data, partCol, from.MaxValue}, nil + return &rangeColumnsPruner{data, partCol, from.MaxValue}, nil } -func (p *rangeColumnPruner) fullRange() partitionRangeOR { +func (p *rangeColumnsPruner) fullRange() partitionRangeOR { return fullRange(len(p.data)) } -func (p *rangeColumnPruner) partitionRangeForExpr(sctx sessionctx.Context, expr expression.Expression) (int, int, bool) { +func (p *rangeColumnsPruner) partitionRangeForExpr(sctx sessionctx.Context, expr expression.Expression) (int, int, bool) { op, ok := expr.(*expression.ScalarFunction) if !ok { return 0, len(p.data), false @@ -822,7 +819,7 @@ func (p *rangeColumnPruner) partitionRangeForExpr(sctx sessionctx.Context, expr return start, end, true } -func (p *rangeColumnPruner) pruneUseBinarySearch(sctx sessionctx.Context, op string, data *expression.Constant) (start int, end int) { +func (p *rangeColumnsPruner) pruneUseBinarySearch(sctx sessionctx.Context, op string, data *expression.Constant) (start int, end int) { var err error var isNull bool compare := func(ith int, op string, v *expression.Constant) bool { diff --git a/table/tables/partition.go b/table/tables/partition.go index f472964058eaf..ac3108aa58a1d 100644 --- a/table/tables/partition.go +++ b/table/tables/partition.go @@ -124,17 +124,11 @@ type PartitionExpr struct { // ForRangeColumnsPruning is used for range partition pruning. type ForRangeColumnsPruning struct { LessThan []expression.Expression - Column ast.ExprNode MaxValue bool } func dataForRangeColumnsPruning(ctx sessionctx.Context, pi *model.PartitionInfo, schema *expression.Schema, names []*types.FieldName, p *parser.Parser) (*ForRangeColumnsPruning, error) { - col, err := parseExpr(p, pi.Columns[0].L) - if err != nil { - return nil, errors.Trace(err) - } var res ForRangeColumnsPruning - res.Column = col res.LessThan = make([]expression.Expression, len(pi.Definitions)) for i := 0; i < len(pi.Definitions); i++ { if strings.EqualFold(pi.Definitions[i].LessThan[0], "MAXVALUE") { From 17cf2060b6369145a6c4fd23a2a1cd9dd679e615 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Tue, 26 May 2020 11:20:53 +0800 Subject: [PATCH 43/74] store/tikv: use resolve lock lite for snapshot.Get() (#17373) --- store/tikv/2pc_test.go | 2 +- store/tikv/coprocessor.go | 10 +++++++++- store/tikv/lock_resolver.go | 21 +++++++++++++-------- store/tikv/lock_test.go | 4 ++-- store/tikv/snapshot.go | 1 + 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/store/tikv/2pc_test.go b/store/tikv/2pc_test.go index ab36004414a5d..5c0a08663f8ef 100644 --- a/store/tikv/2pc_test.go +++ b/store/tikv/2pc_test.go @@ -1004,7 +1004,7 @@ func (s *testCommitterSuite) TestResolveMixed(c *C) { time.Sleep(time.Duration(atomic.LoadUint64(&ManagedLockTTL)) * time.Millisecond) optimisticLockInfo := s.getLockInfo(c, optimisticLockKey) lock := NewLock(optimisticLockInfo) - err = s.store.lockResolver.resolveLock(NewBackoffer(ctx, pessimisticLockMaxBackoff), lock, TxnStatus{}, cleanTxns) + err = s.store.lockResolver.resolveLock(NewBackoffer(ctx, pessimisticLockMaxBackoff), lock, TxnStatus{}, false, cleanTxns) c.Assert(err, IsNil) // txn2 tries to lock the pessimisticLockKey, the lock should has been resolved in clean whole region resolve diff --git a/store/tikv/coprocessor.go b/store/tikv/coprocessor.go index b87f69586f792..7c4d8c7d917a7 100644 --- a/store/tikv/coprocessor.go +++ b/store/tikv/coprocessor.go @@ -797,11 +797,19 @@ type clientHelper struct { *RegionCache *minCommitTSPushed Client + resolveLite bool } // ResolveLocks wraps the ResolveLocks function and store the resolved result. func (ch *clientHelper) ResolveLocks(bo *Backoffer, callerStartTS uint64, locks []*Lock) (int64, error) { - msBeforeTxnExpired, resolvedLocks, err := ch.LockResolver.ResolveLocks(bo, callerStartTS, locks) + var err error + var resolvedLocks []uint64 + var msBeforeTxnExpired int64 + if ch.resolveLite { + msBeforeTxnExpired, resolvedLocks, err = ch.LockResolver.resolveLocksLite(bo, callerStartTS, locks) + } else { + msBeforeTxnExpired, resolvedLocks, err = ch.LockResolver.ResolveLocks(bo, callerStartTS, locks) + } if err != nil { return msBeforeTxnExpired, err } diff --git a/store/tikv/lock_resolver.go b/store/tikv/lock_resolver.go index 9e002211d8325..ec8246ec797e6 100644 --- a/store/tikv/lock_resolver.go +++ b/store/tikv/lock_resolver.go @@ -282,10 +282,14 @@ func (lr *LockResolver) BatchResolveLocks(bo *Backoffer, locks []*Lock, loc Regi // 3) Send `ResolveLock` cmd to the lock's region to resolve all locks belong to // the same transaction. func (lr *LockResolver) ResolveLocks(bo *Backoffer, callerStartTS uint64, locks []*Lock) (int64, []uint64 /*pushed*/, error) { - return lr.resolveLocks(bo, callerStartTS, locks, false) + return lr.resolveLocks(bo, callerStartTS, locks, false, false) } -func (lr *LockResolver) resolveLocks(bo *Backoffer, callerStartTS uint64, locks []*Lock, forWrite bool) (int64, []uint64 /*pushed*/, error) { +func (lr *LockResolver) resolveLocksLite(bo *Backoffer, callerStartTS uint64, locks []*Lock) (int64, []uint64 /*pushed*/, error) { + return lr.resolveLocks(bo, callerStartTS, locks, false, true) +} + +func (lr *LockResolver) resolveLocks(bo *Backoffer, callerStartTS uint64, locks []*Lock, forWrite bool, lite bool) (int64, []uint64 /*pushed*/, error) { var msBeforeTxnExpired txnExpireTime if len(locks) == 0 { return msBeforeTxnExpired.value(), nil, nil @@ -327,7 +331,7 @@ func (lr *LockResolver) resolveLocks(bo *Backoffer, callerStartTS uint64, locks if l.LockType == kvrpcpb.Op_PessimisticLock { err = lr.resolvePessimisticLock(bo, l, cleanRegions) } else { - err = lr.resolveLock(bo, l, status, cleanRegions) + err = lr.resolveLock(bo, l, status, lite, cleanRegions) } if err != nil { msBeforeTxnExpired.update(0) @@ -371,7 +375,7 @@ func (lr *LockResolver) resolveLocks(bo *Backoffer, callerStartTS uint64, locks } func (lr *LockResolver) resolveLocksForWrite(bo *Backoffer, callerStartTS uint64, locks []*Lock) (int64, error) { - msBeforeTxnExpired, _, err := lr.resolveLocks(bo, callerStartTS, locks, true) + msBeforeTxnExpired, _, err := lr.resolveLocks(bo, callerStartTS, locks, true, false) return msBeforeTxnExpired, err } @@ -564,9 +568,9 @@ func (lr *LockResolver) getTxnStatus(bo *Backoffer, txnID uint64, primary []byte } } -func (lr *LockResolver) resolveLock(bo *Backoffer, l *Lock, status TxnStatus, cleanRegions map[RegionVerID]struct{}) error { +func (lr *LockResolver) resolveLock(bo *Backoffer, l *Lock, status TxnStatus, lite bool, cleanRegions map[RegionVerID]struct{}) error { tikvLockResolverCountWithResolveLocks.Inc() - cleanWholeRegion := l.TxnSize >= bigTxnThreshold + resolveLite := lite || l.TxnSize < bigTxnThreshold for { loc, err := lr.store.GetRegionCache().LocateKey(bo, l.Key) if err != nil { @@ -581,7 +585,8 @@ func (lr *LockResolver) resolveLock(bo *Backoffer, l *Lock, status TxnStatus, cl if status.IsCommitted() { lreq.CommitVersion = status.CommitTS() } - if l.TxnSize < bigTxnThreshold { + + if resolveLite { // Only resolve specified keys when it is a small transaction, // prevent from scanning the whole region in this case. tikvLockResolverCountWithResolveLockLite.Inc() @@ -615,7 +620,7 @@ func (lr *LockResolver) resolveLock(bo *Backoffer, l *Lock, status TxnStatus, cl logutil.BgLogger().Error("resolveLock error", zap.Error(err)) return err } - if cleanWholeRegion { + if !resolveLite { cleanRegions[loc.Region] = struct{}{} } return nil diff --git a/store/tikv/lock_test.go b/store/tikv/lock_test.go index 8160aa652a871..3113be2d31cc3 100644 --- a/store/tikv/lock_test.go +++ b/store/tikv/lock_test.go @@ -225,7 +225,7 @@ func (s *testLockSuite) TestCheckTxnStatusTTL(c *C) { lock := s.mustGetLock(c, []byte("key")) status = TxnStatus{} cleanRegions := make(map[RegionVerID]struct{}) - err = newLockResolver(s.store).resolveLock(bo, lock, status, cleanRegions) + err = newLockResolver(s.store).resolveLock(bo, lock, status, false, cleanRegions) c.Assert(err, IsNil) // Check its status is rollbacked. @@ -261,7 +261,7 @@ func (s *testLockSuite) TestTxnHeartBeat(c *C) { lock := s.mustGetLock(c, []byte("key")) status := TxnStatus{ttl: newTTL} cleanRegions := make(map[RegionVerID]struct{}) - err = newLockResolver(s.store).resolveLock(bo, lock, status, cleanRegions) + err = newLockResolver(s.store).resolveLock(bo, lock, status, false, cleanRegions) c.Assert(err, IsNil) newTTL, err = sendTxnHeartBeat(bo, s.store, []byte("key"), txn.StartTS(), 6666) diff --git a/store/tikv/snapshot.go b/store/tikv/snapshot.go index 267653c21223a..2376c82adde5a 100644 --- a/store/tikv/snapshot.go +++ b/store/tikv/snapshot.go @@ -331,6 +331,7 @@ func (s *tikvSnapshot) get(bo *Backoffer, k kv.Key) ([]byte, error) { RegionCache: s.store.regionCache, minCommitTSPushed: &s.minCommitTSPushed, Client: s.store.client, + resolveLite: true, } req := tikvrpc.NewReplicaReadRequest(tikvrpc.CmdGet, From 72a789a24730e0218ce75350099e30a14431a433 Mon Sep 17 00:00:00 2001 From: lawyerphx Date: Tue, 26 May 2020 13:10:02 +0800 Subject: [PATCH 44/74] planner, infoschema : add columns about plan cache in 'performance_schema.events_statements_summary_by_digest' (#16476) --- executor/adapter.go | 1 + infoschema/perfschema/const.go | 2 ++ infoschema/tables.go | 2 ++ infoschema/tables_test.go | 41 ++++++++++++++++++++++ util/stmtsummary/statement_summary.go | 28 +++++++++++---- util/stmtsummary/statement_summary_test.go | 2 +- util/testkit/testkit.go | 11 ++++-- 7 files changed, 77 insertions(+), 10 deletions(-) diff --git a/executor/adapter.go b/executor/adapter.go index 5f2419fac00b0..9110c74e21258 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -981,5 +981,6 @@ func (a *ExecStmt) SummaryStmt(succ bool) { StartTime: sessVars.StartTime, IsInternal: sessVars.InRestrictedSQL, Succeed: succ, + PlanInCache: sessVars.FoundInPlanCache, }) } diff --git a/infoschema/perfschema/const.go b/infoschema/perfschema/const.go index 5ce6ab82c8278..86a5f1766739f 100644 --- a/infoschema/perfschema/const.go +++ b/infoschema/perfschema/const.go @@ -413,6 +413,8 @@ const tableEventsStatementsSummaryByDigest = "CREATE TABLE if not exists perform "SUM_NO_GOOD_INDEX_USED bigint unsigned NOT NULL," + "FIRST_SEEN timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000'," + "LAST_SEEN timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000'," + + "PLAN_IN_CACHE bool NOT NULL," + + "PLAN_CACHE_HITS bigint unsigned NOT NULL," + "QUANTILE_95 bigint unsigned NOT NULL," + "QUANTILE_99 bigint unsigned NOT NULL," + "QUANTILE_999 bigint unsigned NOT NULL," + diff --git a/infoschema/tables.go b/infoschema/tables.go index a142ba10fa448..cae360b74718d 100644 --- a/infoschema/tables.go +++ b/infoschema/tables.go @@ -1098,6 +1098,8 @@ var tableStatementsSummaryCols = []columnInfo{ {name: "AVG_AFFECTED_ROWS", tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rows affected"}, {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the first time"}, {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the last time"}, + {name: "PLAN_IN_CACHE", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the last statement hit plan cache"}, + {name: "PLAN_CACHE_HITS", tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag, comment: "The number of times these statements hit plan cache"}, {name: "QUERY_SAMPLE_TEXT", tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled original statement"}, {name: "PREV_SAMPLE_TEXT", tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The previous statement before commit"}, {name: "PLAN_DIGEST", tp: mysql.TypeVarchar, size: 64, comment: "Digest of its execution plan"}, diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index 08d453619192f..484d9375d2da3 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -16,6 +16,7 @@ package infoschema_test import ( "crypto/tls" "fmt" + "math" "net" "net/http/httptest" "os" @@ -36,11 +37,13 @@ import ( "github.com/pingcap/tidb/infoschema" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/meta/autoid" + plannercore "github.com/pingcap/tidb/planner/core" "github.com/pingcap/tidb/server" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/helper" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/util/kvcache" "github.com/pingcap/tidb/util/pdapi" "github.com/pingcap/tidb/util/set" "github.com/pingcap/tidb/util/testkit" @@ -84,6 +87,18 @@ func (s *testTableSuiteBase) newTestKitWithRoot(c *C) *testkit.TestKit { return tk } +func (s *testTableSuiteBase) newTestKitWithPlanCache(c *C) *testkit.TestKit { + tk := testkit.NewTestKit(c, s.store) + var err error + tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{ + PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64), + }) + c.Assert(err, IsNil) + tk.GetConnectionID() + c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) + return tk +} + type testClusterTableSuite struct { *testTableSuiteBase rpcserver *grpc.Server @@ -1249,3 +1264,29 @@ func (s *testTableSuite) TestStmtSummaryPreparedStatements(c *C) { from information_schema.statements_summary where digest_text like "select ?"`).Check(testkit.Rows("1")) } + +func (s *testTableSuite) TestPerformanceSchemaforPlanCache(c *C) { + orgEnable := plannercore.PreparedPlanCacheEnabled() + defer func() { + plannercore.SetPreparedPlanCache(orgEnable) + }() + plannercore.SetPreparedPlanCache(true) + + tk := s.newTestKitWithPlanCache(c) + + // Clear summaries. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("prepare stmt from 'select * from t'") + tk.MustExec("execute stmt") + tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from t'").Check( + testkit.Rows("0 0")) + tk.MustExec("execute stmt") + tk.MustExec("execute stmt") + tk.MustExec("execute stmt") + tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from t'").Check( + testkit.Rows("3 1")) +} diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index 04c1f741a692f..98f07ca3becd1 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -168,6 +168,9 @@ type stmtSummaryByDigestElement struct { firstSeen time.Time // The last time this type of SQL executes. lastSeen time.Time + // plan cache + planInCache bool + planCacheHits int64 } // StmtExecInfo records execution information of each statement. @@ -192,6 +195,7 @@ type StmtExecInfo struct { StartTime time.Time IsInternal bool Succeed bool + PlanInCache bool } // newStmtSummaryByDigestMap creates an empty stmtSummaryByDigestMap. @@ -568,13 +572,15 @@ func newStmtSummaryByDigestElement(sei *StmtExecInfo, beginTime int64, intervalS // PrevSQL is already truncated to cfg.Log.QueryLogMaxLen. prevSQL: sei.PrevSQL, // samplePlan needs to be decoded so it can't be truncated. - samplePlan: sei.PlanGenerator(), - indexNames: sei.StmtCtx.IndexNames, - minLatency: sei.TotalLatency, - firstSeen: sei.StartTime, - lastSeen: sei.StartTime, - backoffTypes: make(map[fmt.Stringer]int), - authUsers: make(map[string]struct{}), + samplePlan: sei.PlanGenerator(), + indexNames: sei.StmtCtx.IndexNames, + minLatency: sei.TotalLatency, + firstSeen: sei.StartTime, + lastSeen: sei.StartTime, + backoffTypes: make(map[fmt.Stringer]int), + authUsers: make(map[string]struct{}), + planInCache: false, + planCacheHits: 0, } ssElement.add(sei, intervalSeconds) return ssElement @@ -721,6 +727,12 @@ func (ssElement *stmtSummaryByDigestElement) add(sei *StmtExecInfo, intervalSeco commitDetails.Mu.Unlock() } + //plan cache + if sei.PlanInCache { + ssElement.planInCache = true + ssElement.planCacheHits += 1 + } + // other ssElement.sumAffectedRows += sei.StmtCtx.AffectedRows() ssElement.sumMem += sei.MemMax @@ -815,6 +827,8 @@ func (ssElement *stmtSummaryByDigestElement) toDatum(ssbd *stmtSummaryByDigest) avgFloat(int64(ssElement.sumAffectedRows), ssElement.execCount), types.NewTime(types.FromGoTime(ssElement.firstSeen), mysql.TypeTimestamp, 0), types.NewTime(types.FromGoTime(ssElement.lastSeen), mysql.TypeTimestamp, 0), + ssElement.planInCache, + ssElement.planCacheHits, ssElement.sampleSQL, ssElement.prevSQL, ssbd.planDigest, diff --git a/util/stmtsummary/statement_summary_test.go b/util/stmtsummary/statement_summary_test.go index 1b8eb6f2d98cb..5dde31d1d1308 100644 --- a/util/stmtsummary/statement_summary_test.go +++ b/util/stmtsummary/statement_summary_test.go @@ -606,7 +606,7 @@ func (s *testStmtSummarySuite) TestToDatum(c *C) { stmtExecInfo1.ExecDetail.CommitDetail.PrewriteRegionNum, stmtExecInfo1.ExecDetail.CommitDetail.PrewriteRegionNum, stmtExecInfo1.ExecDetail.CommitDetail.TxnRetry, stmtExecInfo1.ExecDetail.CommitDetail.TxnRetry, 1, "txnLock:1", stmtExecInfo1.MemMax, stmtExecInfo1.MemMax, stmtExecInfo1.StmtCtx.AffectedRows(), - t, t, stmtExecInfo1.OriginalSQL, stmtExecInfo1.PrevSQL, "plan_digest", ""} + t, t, 0, 0, stmtExecInfo1.OriginalSQL, stmtExecInfo1.PrevSQL, "plan_digest", ""} match(c, datums[0], expectedDatum...) datums = s.ssMap.ToHistoryDatum(nil, true) diff --git a/util/testkit/testkit.go b/util/testkit/testkit.go index cd7c389385437..1ae061c3b0adc 100644 --- a/util/testkit/testkit.go +++ b/util/testkit/testkit.go @@ -129,14 +129,21 @@ func NewTestKitWithInit(c *check.C, store kv.Storage) *TestKit { var connectionID uint64 +// GetConnectionID get the connection ID for tk.Se +func (tk *TestKit) GetConnectionID() { + if tk.Se != nil { + id := atomic.AddUint64(&connectionID, 1) + tk.Se.SetConnectionID(id) + } +} + // Exec executes a sql statement. func (tk *TestKit) Exec(sql string, args ...interface{}) (sqlexec.RecordSet, error) { var err error if tk.Se == nil { tk.Se, err = session.CreateSession4Test(tk.store) tk.c.Assert(err, check.IsNil) - id := atomic.AddUint64(&connectionID, 1) - tk.Se.SetConnectionID(id) + tk.GetConnectionID() } ctx := context.Background() if len(args) == 0 { From b923b9e5de7d198c1bde3feb03eefc3d6a9b5fbb Mon Sep 17 00:00:00 2001 From: crazycs Date: Tue, 26 May 2020 13:13:40 +0800 Subject: [PATCH 45/74] util/admin: support admin cleanup index on the partition table (#17203) --- executor/admin.go | 43 +++++++++++++++++++---- executor/admin_test.go | 79 ++++++++++++++++++++++++++++++++++++++++++ executor/builder.go | 1 + 3 files changed, 117 insertions(+), 6 deletions(-) diff --git a/executor/admin.go b/executor/admin.go index d50593042dc9e..a6e846635a8b2 100644 --- a/executor/admin.go +++ b/executor/admin.go @@ -491,8 +491,9 @@ type CleanupIndexExec struct { done bool removeCnt uint64 - index table.Index - table table.Table + index table.Index + table table.Table + physicalID int64 idxCols []*model.ColumnInfo idxColFieldTypes []*types.FieldType @@ -606,6 +607,34 @@ func (e *CleanupIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { if e.done { return nil } + var err error + if tbl, ok := e.table.(table.PartitionedTable); ok { + pi := e.table.Meta().GetPartitionInfo() + for _, p := range pi.Definitions { + e.table = tbl.GetPartition(p.ID) + e.index = tables.GetWritableIndexByName(e.index.Meta().Name.L, e.table) + e.physicalID = p.ID + err = e.init() + if err != nil { + return err + } + err = e.cleanTableIndex(ctx) + if err != nil { + return err + } + } + } else { + err = e.cleanTableIndex(ctx) + if err != nil { + return err + } + } + e.done = true + req.AppendUint64(0, e.removeCnt) + return nil +} + +func (e *CleanupIndexExec) cleanTableIndex(ctx context.Context) error { for { errInTxn := kv.RunInNewTxn(e.ctx.GetStore(), true, func(txn kv.Transaction) error { err := e.fetchIndex(ctx, txn) @@ -634,8 +663,6 @@ func (e *CleanupIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { delete(e.idxValues, k) } } - e.done = true - req.AppendUint64(0, e.removeCnt) return nil } @@ -647,7 +674,7 @@ func (e *CleanupIndexExec) buildIndexScan(ctx context.Context, txn kv.Transactio sc := e.ctx.GetSessionVars().StmtCtx var builder distsql.RequestBuilder ranges := ranger.FullRange() - kvReq, err := builder.SetIndexRanges(sc, e.table.Meta().ID, e.index.Meta().ID, ranges). + kvReq, err := builder.SetIndexRanges(sc, e.physicalID, e.index.Meta().ID, ranges). SetDAGRequest(dagPB). SetStartTS(txn.StartTS()). SetKeepOrder(true). @@ -672,6 +699,10 @@ func (e *CleanupIndexExec) Open(ctx context.Context) error { if err := e.baseExecutor.Open(ctx); err != nil { return err } + return e.init() +} + +func (e *CleanupIndexExec) init() error { e.idxChunk = chunk.New(e.getIdxColTypes(), e.initCap, e.maxChunkSize) e.idxValues = make(map[int64][][]types.Datum, e.batchSize) e.batchKeys = make([]kv.Key, 0, e.batchSize) @@ -709,7 +740,7 @@ func (e *CleanupIndexExec) buildIdxDAGPB(txn kv.Transaction) (*tipb.DAGRequest, func (e *CleanupIndexExec) constructIndexScanPB() *tipb.Executor { idxExec := &tipb.IndexScan{ - TableId: e.table.Meta().ID, + TableId: e.physicalID, IndexId: e.index.Meta().ID, Columns: util.ColumnsToProto(e.idxCols, e.table.Meta().PKIsHandle), } diff --git a/executor/admin_test.go b/executor/admin_test.go index fe5a40c6e9368..da6bd3e260cdd 100644 --- a/executor/admin_test.go +++ b/executor/admin_test.go @@ -386,6 +386,85 @@ func (s *testSuite5) TestAdminCleanupIndex(c *C) { tk.MustExec("admin check table admin_test") } +func (s *testSuite5) TestAdminCleanupIndexForPartitionTable(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + + getTable := func() table.Table { + s.ctx = mock.NewContext() + s.ctx.Store = s.store + is := s.domain.InfoSchema() + dbName := model.NewCIStr("test") + tblName := model.NewCIStr("admin_test") + tbl, err := is.TableByName(dbName, tblName) + c.Assert(err, IsNil) + return tbl + } + + checkFunc := func(tbl table.Table, pid int64, idxValue, handle int) { + idxInfo2 := tbl.Meta().FindIndexByName("c2") + indexOpr2 := tables.NewIndex(pid, tbl.Meta(), idxInfo2) + idxInfo3 := tbl.Meta().FindIndexByName("c3") + indexOpr3 := tables.NewIndex(pid, tbl.Meta(), idxInfo3) + + txn, err := s.store.Begin() + c.Assert(err, IsNil) + _, err = indexOpr2.Create(s.ctx, txn, types.MakeDatums(idxValue), kv.IntHandle(handle)) + c.Assert(err, IsNil) + _, err = indexOpr3.Create(s.ctx, txn, types.MakeDatums(idxValue), kv.IntHandle(handle)) + c.Assert(err, IsNil) + err = txn.Commit(context.Background()) + c.Assert(err, IsNil) + + err = tk.ExecToErr("admin check table admin_test") + c.Assert(err, NotNil) + + r := tk.MustQuery("SELECT COUNT(*) FROM admin_test USE INDEX(c2)") + r.Check(testkit.Rows("4")) + r = tk.MustQuery("admin cleanup index admin_test c2") + r.Check(testkit.Rows("1")) + r = tk.MustQuery("SELECT COUNT(*) FROM admin_test USE INDEX(c2)") + r.Check(testkit.Rows("3")) + + r = tk.MustQuery("SELECT COUNT(*) FROM admin_test USE INDEX(c3)") + r.Check(testkit.Rows("4")) + r = tk.MustQuery("admin cleanup index admin_test c3") + r.Check(testkit.Rows("1")) + r = tk.MustQuery("SELECT COUNT(*) FROM admin_test USE INDEX(c3)") + r.Check(testkit.Rows("3")) + tk.MustExec("admin check table admin_test") + } + + // Test for hash partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, primary key (c2), unique index c2(c2), index c3(c3)) partition by hash(c2) partitions 3;") + tk.MustExec("insert admin_test (c2, c3) values (0, 0), (1, 1), (2, 2)") + r := tk.MustQuery("admin cleanup index admin_test c2") + r.Check(testkit.Rows("0")) + tbl := getTable() + pi := tbl.Meta().GetPartitionInfo() + c.Assert(pi, NotNil) + for i, p := range pi.Definitions { + checkFunc(tbl, p.ID, i+6, i+6) + } + + // Test for range partition table. + tk.MustExec("drop table if exists admin_test") + tk.MustExec(`create table admin_test (c1 int, c2 int, c3 int default 1, primary key (c2), unique index c2 (c2), index c3(c3)) PARTITION BY RANGE ( c2 ) ( + PARTITION p0 VALUES LESS THAN (5), + PARTITION p1 VALUES LESS THAN (10), + PARTITION p2 VALUES LESS THAN (MAXVALUE))`) + tk.MustExec("insert admin_test (c1, c2) values (0, 0), (6, 6), (12, 12)") + r = tk.MustQuery("admin cleanup index admin_test c2") + r.Check(testkit.Rows("0")) + tbl = getTable() + pi = tbl.Meta().GetPartitionInfo() + c.Assert(pi, NotNil) + for i, p := range pi.Definitions { + checkFunc(tbl, p.ID, i*6+1, i*6+1) + } +} + func (s *testSuite5) TestAdminCleanupIndexPKNotHandle(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/executor/builder.go b/executor/builder.go index e92df802d6acd..650faafd1d570 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -470,6 +470,7 @@ func (b *executorBuilder) buildCleanupIndex(v *plannercore.CleanupIndex) Executo idxCols: buildCleanupIndexCols(tblInfo, index.Meta()), index: index, table: t, + physicalID: t.Meta().ID, batchSize: 20000, } return e From 8da89b7af82640e43530eae38421a11250f777cc Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Tue, 26 May 2020 13:58:14 +0800 Subject: [PATCH 46/74] expression: speed up TestIssue16697 (#17400) --- expression/integration_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index c363106f7ff71..4c8f355b4c3e7 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -6394,12 +6394,12 @@ func (s *testIntegrationSuite) TestIssue16697(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(11) DEFAULT NULL,`b` int(11) DEFAULT NULL)") - tk.MustExec("insert into t values (1, 1)") - for i := 0; i < 8; i++ { + tk.MustExec("CREATE TABLE t (v varchar(1024))") + tk.MustExec("insert into t values (space(1024))") + for i := 0; i < 5; i++ { tk.MustExec("insert into t select * from t") } - rows := tk.MustQuery("explain analyze select t1.a, t1.a +1 from t t1 join t t2 join t t3 order by t1.a").Rows() + rows := tk.MustQuery("explain analyze select * from t").Rows() for _, row := range rows { line := fmt.Sprintf("%v", row) if strings.Contains(line, "Projection") { From 8369ffd500f3fb235d8b584ac4298b2e59d8db55 Mon Sep 17 00:00:00 2001 From: Soup Date: Tue, 26 May 2020 15:38:29 +0800 Subject: [PATCH 47/74] docs: Add TiFlash into architecture image (#17050) --- docs/architecture.png | Bin 202756 -> 232815 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/architecture.png b/docs/architecture.png index 51d4f57aa7dd2deeb320dc8953ea43585217edb9..c9f9ecbb593e3f6c4173a9a59ad2a9aa37be5d06 100644 GIT binary patch literal 232815 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≪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

D_H#wn2=r6nq|05bw%^?`B1mP(3Cl9$ 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�MslM{$C)wV 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 T;&tD14#>iYixnHvOP literal 202756 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 OPxrLJsr-{yZvG2#^zNkq From f72b5984dd33c5ca5e37bf812b8ecb7551fed0b0 Mon Sep 17 00:00:00 2001 From: wjHuang Date: Tue, 26 May 2020 15:48:13 +0800 Subject: [PATCH 48/74] executor: set the DDL query string instead of `execute` (#17407) --- executor/adapter.go | 1 + session/session_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/executor/adapter.go b/executor/adapter.go index 9110c74e21258..9db8085ea8583 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -718,6 +718,7 @@ func (a *ExecStmt) buildExecutor() (Executor, error) { if err != nil { return nil, err } + a.Ctx.SetValue(sessionctx.QueryString, executorExec.stmt.Text()) a.OutputNames = executorExec.outputNames a.isPreparedStmt = true a.Plan = executorExec.plan diff --git a/session/session_test.go b/session/session_test.go index a5d2fddc762cb..a0d2f69b08161 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -313,6 +313,18 @@ func (s *testSessionSuite) TestQueryString(c *C) { c.Assert(err, IsNil) qs := tk.Se.Value(sessionctx.QueryString) c.Assert(qs.(string), Equals, "CREATE TABLE t2(id bigint PRIMARY KEY, age int)") + + // Test execution of DDL through the "Execute" interface. + _, err = tk.Se.Execute(context.Background(), "use test;") + c.Assert(err, IsNil) + _, err = tk.Se.Execute(context.Background(), "drop table t2") + c.Assert(err, IsNil) + _, err = tk.Se.Execute(context.Background(), "prepare stmt from 'CREATE TABLE t2(id bigint PRIMARY KEY, age int)'") + c.Assert(err, IsNil) + _, err = tk.Se.Execute(context.Background(), "execute stmt") + c.Assert(err, IsNil) + qs = tk.Se.Value(sessionctx.QueryString) + c.Assert(qs.(string), Equals, "CREATE TABLE t2(id bigint PRIMARY KEY, age int)") } func (s *testSessionSuite) TestAffectedRows(c *C) { From a3d5082094b0ff6d08236e308891c39419d196dc Mon Sep 17 00:00:00 2001 From: tangenta Date: Tue, 26 May 2020 20:32:10 +0800 Subject: [PATCH 49/74] sessionctx, executor: add session var to control explicit insertion on auto_random column (#17102) --- ddl/serial_test.go | 17 +++++++++++++++++ executor/ddl_test.go | 1 + executor/insert_common.go | 4 ++++ executor/insert_test.go | 2 ++ executor/seqtest/seq_executor_test.go | 1 + meta/autoid/errors.go | 2 ++ session/session.go | 1 + sessionctx/variable/session.go | 6 ++++++ sessionctx/variable/sysvar.go | 1 + sessionctx/variable/tidb_vars.go | 4 ++++ sessionctx/variable/varsutil.go | 3 ++- sessionctx/variable/varsutil_test.go | 1 + 12 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ddl/serial_test.go b/ddl/serial_test.go index 5f1491f2674d3..ffdd07206bd80 100644 --- a/ddl/serial_test.go +++ b/ddl/serial_test.go @@ -940,6 +940,23 @@ func (s *testSerialSuite) TestAutoRandom(c *C) { assertShowWarningCorrect("create table t (a bigint auto_random(15) primary key)", 281474976710655) assertShowWarningCorrect("create table t (a bigint unsigned auto_random(15) primary key)", 562949953421311) + // Test insert into auto_random column explicitly is not allowed by default. + assertExplicitInsertDisallowed := func(sql string) { + assertInvalidAutoRandomErr(sql, autoid.AutoRandomExplicitInsertDisabledErrMsg) + } + tk.MustExec("set @@allow_auto_random_explicit_insert = false") + mustExecAndDrop("create table t (a bigint auto_random primary key)", func() { + assertExplicitInsertDisallowed("insert into t values (1)") + assertExplicitInsertDisallowed("insert into t values (3)") + tk.MustExec("insert into t values()") + }) + tk.MustExec("set @@allow_auto_random_explicit_insert = true") + mustExecAndDrop("create table t (a bigint auto_random primary key)", func() { + tk.MustExec("insert into t values(1)") + tk.MustExec("insert into t values(3)") + tk.MustExec("insert into t values()") + }) + // Disallow using it when allow-auto-random is not enabled. config.GetGlobalConfig().Experimental.AllowAutoRandom = false assertExperimentDisabled("create table auto_random_table (a int primary key auto_random(3))") diff --git a/executor/ddl_test.go b/executor/ddl_test.go index 4413acc56d85b..4bbdf16ea51a7 100644 --- a/executor/ddl_test.go +++ b/executor/ddl_test.go @@ -844,6 +844,7 @@ func (s *testAutoRandomSuite) TestAutoRandomBitsData(c *C) { testutil.ConfigTestUtils.SetupAutoRandomTestConfig() defer testutil.ConfigTestUtils.RestoreAutoRandomTestConfig() + tk.MustExec("set @@allow_auto_random_explicit_insert = true") tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") for i := 0; i < 100; i++ { diff --git a/executor/insert_common.go b/executor/insert_common.go index 0273efbb9baad..9744664247334 100644 --- a/executor/insert_common.go +++ b/executor/insert_common.go @@ -22,6 +22,7 @@ import ( "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/ddl" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/meta/autoid" @@ -847,6 +848,9 @@ func (e *InsertValues) adjustAutoRandomDatum(ctx context.Context, d types.Datum, } // Use the value if it's not null and not 0. if recordID != 0 { + if !e.ctx.GetSessionVars().AllowAutoRandExplicitInsert { + return types.Datum{}, ddl.ErrInvalidAutoRandom.GenWithStackByArgs(autoid.AutoRandomExplicitInsertDisabledErrMsg) + } err = e.rebaseAutoRandomID(recordID, &c.FieldType) if err != nil { return types.Datum{}, err diff --git a/executor/insert_test.go b/executor/insert_test.go index 666eb21c9dc35..182476aa45e73 100644 --- a/executor/insert_test.go +++ b/executor/insert_test.go @@ -1106,6 +1106,8 @@ func (s *testSuite9) TestAutoRandomIDExplicit(c *C) { } tk := testkit.NewTestKit(c, s.store) + tk.MustExec("set @@allow_auto_random_explicit_insert = true") + tk.MustExec(`use test`) tk.MustExec(`drop table if exists ar`) tk.MustExec(`create table ar (id bigint key auto_random, name char(10))`) diff --git a/executor/seqtest/seq_executor_test.go b/executor/seqtest/seq_executor_test.go index 8d936390e196c..bce047c30736e 100644 --- a/executor/seqtest/seq_executor_test.go +++ b/executor/seqtest/seq_executor_test.go @@ -832,6 +832,7 @@ func HelperTestAdminShowNextID(c *C, s *seqTestSuite, str string) { oldAutoRandom := config.GetGlobalConfig().Experimental.AllowAutoRandom config.GetGlobalConfig().Experimental.AllowAutoRandom = true + tk.MustExec("set @@allow_auto_random_explicit_insert = true") defer func() { config.GetGlobalConfig().Experimental.AllowAutoRandom = oldAutoRandom }() diff --git a/meta/autoid/errors.go b/meta/autoid/errors.go index f241c7badfa7b..f2312bede7a01 100644 --- a/meta/autoid/errors.go +++ b/meta/autoid/errors.go @@ -47,6 +47,8 @@ const ( AutoRandomNonPositive = "the value of auto_random should be positive" // AutoRandomAvailableAllocTimesNote is reported when a table containing auto_random is created. AutoRandomAvailableAllocTimesNote = "Available implicit allocation times: %d" + // AutoRandomExplicitInsertDisabledErrMsg is reported when auto_random column value is explicitly specified, but the session var 'allow_auto_random_explicit_insert' is false. + AutoRandomExplicitInsertDisabledErrMsg = "Explicit insertion on auto_random column is disabled. Try to set @@allow_auto_random_explicit_insert = true." // AutoRandomOnNonBigIntColumn is reported when define auto random to non bigint column AutoRandomOnNonBigIntColumn = "auto_random option must be defined on `bigint` column, but not on `%s` column" ) diff --git a/session/session.go b/session/session.go index 56a3af7cac15a..de5f7e5f4fbe1 100644 --- a/session/session.go +++ b/session/session.go @@ -2053,6 +2053,7 @@ var builtinGlobalVariable = []string{ variable.TiDBEvolvePlanBaselines, variable.TiDBIsolationReadEngines, variable.TiDBStoreLimit, + variable.TiDBAllowAutoRandExplicitInsert, } var ( diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index cb58fd1f79bbe..94f113e7c58df 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -396,6 +396,9 @@ type SessionVars struct { // If value is set to 2 , which means to force to send batch cop for any query. Value is set to 0 means never use batch cop. AllowBatchCop int + // TiDBAllowAutoRandExplicitInsert indicates whether explicit insertion on auto_random column is allowed. + AllowAutoRandExplicitInsert bool + // CorrelationThreshold is the guard to enable row count estimation using column order correlation. CorrelationThreshold float64 @@ -690,6 +693,7 @@ func NewSessionVars() *SessionVars { WindowingUseHighPrecision: true, PrevFoundInPlanCache: DefTiDBFoundInPlanCache, FoundInPlanCache: DefTiDBFoundInPlanCache, + AllowAutoRandExplicitInsert: DefTiDBAllowAutoRandExplicitInsert, } vars.KVVars = kv.NewVariables(&vars.Killed) vars.Concurrency = Concurrency{ @@ -1290,6 +1294,8 @@ func (s *SessionVars) SetSystemVar(name string, val string) error { s.FoundInPlanCache = TiDBOptOn(val) case TiDBEnableCollectExecutionInfo: config.GetGlobalConfig().EnableCollectExecutionInfo = TiDBOptOn(val) + case TiDBAllowAutoRandExplicitInsert: + s.AllowAutoRandExplicitInsert = TiDBOptOn(val) } s.systems[name] = val return nil diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 4067052aeab66..9970dd93b0dc5 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -721,6 +721,7 @@ var defaultSysVars = []*SysVar{ {ScopeSession, TiDBCheckMb4ValueInUTF8, BoolToIntStr(config.GetGlobalConfig().CheckMb4ValueInUTF8)}, {ScopeSession, TiDBFoundInPlanCache, BoolToIntStr(DefTiDBFoundInPlanCache)}, {ScopeSession, TiDBEnableCollectExecutionInfo, BoolToIntStr(logutil.DefaultTiDBEnableSlowLog)}, + {ScopeSession, TiDBAllowAutoRandExplicitInsert, boolToOnOff(DefTiDBAllowAutoRandExplicitInsert)}, } // SynonymsSysVariables is synonyms of system variables. diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index 02af2539b5e3a..03366d209a08a 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -171,6 +171,9 @@ const ( // TiDBFoundInPlanCache indicates whether the last statement was found in plan cache TiDBFoundInPlanCache = "last_plan_from_cache" + + // TiDBAllowAutoRandExplicitInsert indicates whether explicit insertion on auto_random column is allowed. + TiDBAllowAutoRandExplicitInsert = "allow_auto_random_explicit_insert" ) // TiDB system variable names that both in session and global scope. @@ -487,6 +490,7 @@ const ( DefTiDBMetricSchemaRangeDuration = 60 // 60s DefTiDBFoundInPlanCache = false DefTidbEnableCollectExecutionInfo = false + DefTiDBAllowAutoRandExplicitInsert = false ) // Process global variables. diff --git a/sessionctx/variable/varsutil.go b/sessionctx/variable/varsutil.go index e4879dedaa2bb..0d386115e196a 100644 --- a/sessionctx/variable/varsutil.go +++ b/sessionctx/variable/varsutil.go @@ -440,7 +440,8 @@ func ValidateSetSystemVar(vars *SessionVars, name string, value string, scope Sc TiDBLowResolutionTSO, TiDBEnableIndexMerge, TiDBEnableNoopFuncs, TiDBCheckMb4ValueInUTF8, TiDBEnableSlowLog, TiDBRecordPlanInSlowLog, TiDBScatterRegion, TiDBGeneralLog, TiDBConstraintCheckInPlace, - TiDBEnableVectorizedExpression, TiDBFoundInPlanCache, TiDBEnableCollectExecutionInfo: + TiDBEnableVectorizedExpression, TiDBFoundInPlanCache, TiDBEnableCollectExecutionInfo, + TiDBAllowAutoRandExplicitInsert: fallthrough case GeneralLog, AvoidTemporalUpgrade, BigTables, CheckProxyUsers, LogBin, CoreFile, EndMakersInJSON, SQLLogBin, OfflineMode, PseudoSlaveMode, LowPriorityUpdates, diff --git a/sessionctx/variable/varsutil_test.go b/sessionctx/variable/varsutil_test.go index b4b16c095de74..0c24b698e3c88 100644 --- a/sessionctx/variable/varsutil_test.go +++ b/sessionctx/variable/varsutil_test.go @@ -85,6 +85,7 @@ func (s *testVarsutilSuite) TestNewSessionVars(c *C) { c.Assert(vars.TiDBOptJoinReorderThreshold, Equals, DefTiDBOptJoinReorderThreshold) c.Assert(vars.EnableFastAnalyze, Equals, DefTiDBUseFastAnalyze) c.Assert(vars.FoundInPlanCache, Equals, DefTiDBFoundInPlanCache) + c.Assert(vars.AllowAutoRandExplicitInsert, Equals, DefTiDBAllowAutoRandExplicitInsert) assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.Concurrency)) assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.MemQuota)) From 55d9d6ca1e2000ed1dbf5a6f84f6fd6a5fbfdcc5 Mon Sep 17 00:00:00 2001 From: __hidehalo Date: Tue, 26 May 2020 22:03:40 +0800 Subject: [PATCH 50/74] sessionctx,infoschema,executor,util: Show disk usage of a query in slow query and statement summary (#17132) --- executor/adapter.go | 4 ++++ executor/slow_query.go | 4 ++++ executor/slow_query_test.go | 3 ++- infoschema/tables.go | 3 +++ infoschema/tables_test.go | 5 +++-- sessionctx/variable/session.go | 7 +++++++ sessionctx/variable/session_test.go | 3 +++ util/stmtsummary/statement_summary.go | 9 +++++++++ util/stmtsummary/statement_summary_test.go | 11 +++++++++-- 9 files changed, 44 insertions(+), 5 deletions(-) diff --git a/executor/adapter.go b/executor/adapter.go index 9db8085ea8583..31e7a1a64e018 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -830,6 +830,7 @@ func (a *ExecStmt) LogSlowQuery(txnTS uint64, succ bool, hasMoreResults bool) { copTaskInfo := sessVars.StmtCtx.CopTasksDetails() statsInfos := plannercore.GetStatsInfo(a.Plan) memMax := sessVars.StmtCtx.MemTracker.MaxConsumed() + diskMax := sessVars.StmtCtx.DiskTracker.MaxConsumed() _, digest := sessVars.StmtCtx.SQLDigest() _, planDigest := getPlanDigest(a.Ctx, a.Plan) slowItems := &variable.SlowQueryLogItems{ @@ -844,6 +845,7 @@ func (a *ExecStmt) LogSlowQuery(txnTS uint64, succ bool, hasMoreResults bool) { CopTasks: copTaskInfo, ExecDetail: execDetail, MemMax: memMax, + DiskMax: diskMax, Succ: succ, Plan: getPlanTree(a.Plan), PlanDigest: planDigest, @@ -960,6 +962,7 @@ func (a *ExecStmt) SummaryStmt(succ bool) { execDetail := stmtCtx.GetExecDetails() copTaskInfo := stmtCtx.CopTasksDetails() memMax := stmtCtx.MemTracker.MaxConsumed() + diskMax := stmtCtx.DiskTracker.MaxConsumed() stmtsummary.StmtSummaryByDigestMap.AddStatement(&stmtsummary.StmtExecInfo{ SchemaName: strings.ToLower(sessVars.CurrentDB), @@ -979,6 +982,7 @@ func (a *ExecStmt) SummaryStmt(succ bool) { CopTasks: copTaskInfo, ExecDetail: &execDetail, MemMax: memMax, + DiskMax: diskMax, StartTime: sessVars.StartTime, IsInternal: sessVars.InRestrictedSQL, Succeed: succ, diff --git a/executor/slow_query.go b/executor/slow_query.go index 182fc150f23c5..ea20bd735b63f 100644 --- a/executor/slow_query.go +++ b/executor/slow_query.go @@ -339,6 +339,7 @@ type slowQueryTuple struct { maxWaitTime float64 maxWaitAddress string memMax int64 + diskMax int64 prevStmt string sql string isInternal bool @@ -464,6 +465,8 @@ func (st *slowQueryTuple) setFieldValue(tz *time.Location, field, value string, st.planDigest = value case variable.SlowLogQuerySQLStr: st.sql = value + case variable.SlowLogDiskMax: + st.diskMax, err = strconv.ParseInt(value, 10, 64) } if err != nil { return valid, errors.Wrap(err, "Parse slow log at line "+strconv.FormatInt(int64(lineNum), 10)+" failed. Field: `"+field+"`, error") @@ -515,6 +518,7 @@ func (st *slowQueryTuple) convertToDatumRow() []types.Datum { record = append(record, types.NewFloat64Datum(st.maxWaitTime)) record = append(record, types.NewStringDatum(st.maxWaitAddress)) record = append(record, types.NewIntDatum(st.memMax)) + record = append(record, types.NewIntDatum(st.diskMax)) if st.succ { record = append(record, types.NewIntDatum(1)) } else { diff --git a/executor/slow_query_test.go b/executor/slow_query_test.go index 489b74098d2e3..44749e5a4f077 100644 --- a/executor/slow_query_test.go +++ b/executor/slow_query_test.go @@ -54,6 +54,7 @@ func (s *testExecSuite) TestParseSlowLogFile(c *C) { # Cop_proc_avg: 0.1 Cop_proc_p90: 0.2 Cop_proc_max: 0.03 Cop_proc_addr: 127.0.0.1:20160 # Cop_wait_avg: 0.05 Cop_wait_p90: 0.6 Cop_wait_max: 0.8 Cop_wait_addr: 0.0.0.0:20160 # Mem_max: 70724 +# Disk_max: 65536 # Plan_from_cache: true # Succ: false # Plan_digest: 60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4 @@ -76,7 +77,7 @@ select * from t;` } recordString += str } - expectRecordString := "2019-04-28 15:24:04.309074,405888132465033227,,,0,0.216905,0,0,0,0,0,0,0,,0,0,0,0,0,0,0.38,0.021,0,0,0,1,637,0,,,1,42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772,t1:1,t2:2,0.1,0.2,0.03,127.0.0.1:20160,0.05,0.6,0.8,0.0.0.0:20160,70724,0,1,,60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4,update t set i = 1;,select * from t;" + expectRecordString := "2019-04-28 15:24:04.309074,405888132465033227,,,0,0.216905,0,0,0,0,0,0,0,,0,0,0,0,0,0,0.38,0.021,0,0,0,1,637,0,,,1,42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772,t1:1,t2:2,0.1,0.2,0.03,127.0.0.1:20160,0.05,0.6,0.8,0.0.0.0:20160,70724,65536,0,1,,60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4,update t set i = 1;,select * from t;" c.Assert(expectRecordString, Equals, recordString) // fix sql contain '# ' bug diff --git a/infoschema/tables.go b/infoschema/tables.go index cae360b74718d..f7e528a4e4c51 100644 --- a/infoschema/tables.go +++ b/infoschema/tables.go @@ -745,6 +745,7 @@ var slowQueryCols = []columnInfo{ {name: variable.SlowLogCopWaitMax, tp: mysql.TypeDouble, size: 22}, {name: variable.SlowLogCopWaitAddr, tp: mysql.TypeVarchar, size: 64}, {name: variable.SlowLogMemMax, tp: mysql.TypeLonglong, size: 20}, + {name: variable.SlowLogDiskMax, tp: mysql.TypeLonglong, size: 20}, {name: variable.SlowLogSucc, tp: mysql.TypeTiny, size: 1}, {name: variable.SlowLogPlanFromCache, tp: mysql.TypeTiny, size: 1}, {name: variable.SlowLogPlan, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, @@ -1095,6 +1096,8 @@ var tableStatementsSummaryCols = []columnInfo{ {name: "BACKOFF_TYPES", tp: mysql.TypeVarchar, size: 1024, comment: "Types of errors and the number of retries for each type"}, {name: "AVG_MEM", tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average memory(byte) used"}, {name: "MAX_MEM", tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max memory(byte) used"}, + {name: "AVG_DISK", tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average disk space(byte) used"}, + {name: "MAX_DISK", tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max disk space(byte) used"}, {name: "AVG_AFFECTED_ROWS", tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rows affected"}, {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the first time"}, {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the last time"}, diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index 484d9375d2da3..0cf81a222dd0f 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -547,6 +547,7 @@ func prepareSlowLogfile(c *C, slowLogFileName string) { # Cop_proc_avg: 0.1 Cop_proc_p90: 0.2 Cop_proc_max: 0.03 Cop_proc_addr: 127.0.0.1:20160 # Cop_wait_avg: 0.05 Cop_wait_p90: 0.6 Cop_wait_max: 0.8 Cop_wait_addr: 0.0.0.0:20160 # Mem_max: 70724 +# Disk_max: 65536 # Plan_from_cache: true # Succ: true # Plan: abcd @@ -619,10 +620,10 @@ func (s *testTableSuite) TestSlowQuery(c *C) { tk.MustExec("set time_zone = '+08:00';") re := tk.MustQuery("select * from information_schema.slow_query") re.Check(testutil.RowsWithSep("|", - "2019-02-12 19:33:56.571953|406315658548871171|root|127.0.0.1|6|4.895492|0.4|0.2|0.19|0.21|0.01|0|0.18|[txnLock]|0.03|0|15|480|1|8|0.3824278|0.161|0.101|0.092|1.71|1|100001|100000|test||0|42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772|t1:1,t2:2|0.1|0.2|0.03|127.0.0.1:20160|0.05|0.6|0.8|0.0.0.0:20160|70724|1|1|abcd|60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4|update t set i = 2;|select * from t_slim;")) + "2019-02-12 19:33:56.571953|406315658548871171|root|127.0.0.1|6|4.895492|0.4|0.2|0.19|0.21|0.01|0|0.18|[txnLock]|0.03|0|15|480|1|8|0.3824278|0.161|0.101|0.092|1.71|1|100001|100000|test||0|42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772|t1:1,t2:2|0.1|0.2|0.03|127.0.0.1:20160|0.05|0.6|0.8|0.0.0.0:20160|70724|65536|1|1|abcd|60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4|update t set i = 2;|select * from t_slim;")) tk.MustExec("set time_zone = '+00:00';") re = tk.MustQuery("select * from information_schema.slow_query") - re.Check(testutil.RowsWithSep("|", "2019-02-12 11:33:56.571953|406315658548871171|root|127.0.0.1|6|4.895492|0.4|0.2|0.19|0.21|0.01|0|0.18|[txnLock]|0.03|0|15|480|1|8|0.3824278|0.161|0.101|0.092|1.71|1|100001|100000|test||0|42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772|t1:1,t2:2|0.1|0.2|0.03|127.0.0.1:20160|0.05|0.6|0.8|0.0.0.0:20160|70724|1|1|abcd|60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4|update t set i = 2;|select * from t_slim;")) + re.Check(testutil.RowsWithSep("|", "2019-02-12 11:33:56.571953|406315658548871171|root|127.0.0.1|6|4.895492|0.4|0.2|0.19|0.21|0.01|0|0.18|[txnLock]|0.03|0|15|480|1|8|0.3824278|0.161|0.101|0.092|1.71|1|100001|100000|test||0|42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772|t1:1,t2:2|0.1|0.2|0.03|127.0.0.1:20160|0.05|0.6|0.8|0.0.0.0:20160|70724|65536|1|1|abcd|60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4|update t set i = 2;|select * from t_slim;")) // Test for long query. f, err := os.OpenFile(slowLogFileName, os.O_CREATE|os.O_WRONLY, 0644) diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 94f113e7c58df..89b0b6c44a4dc 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -1517,6 +1517,8 @@ const ( SlowLogCopBackoffPrefix = "Cop_backoff_" // SlowLogMemMax is the max number bytes of memory used in this statement. SlowLogMemMax = "Mem_max" + // SlowLogDiskMax is the nax number bytes of disk used in this statement. + SlowLogDiskMax = "Disk_max" // SlowLogPrepared is used to indicate whether this sql execute in prepare. SlowLogPrepared = "Prepared" // SlowLogPlanFromCache is used to indicate whether this plan is from plan cache. @@ -1553,6 +1555,7 @@ type SlowQueryLogItems struct { CopTasks *stmtctx.CopTasksDetails ExecDetail execdetails.ExecDetails MemMax int64 + DiskMax int64 Succ bool Prepared bool PlanFromCache bool @@ -1579,6 +1582,7 @@ type SlowQueryLogItems struct { // # Cop_process: Avg_time: 1s P90_time: 2s Max_time: 3s Max_addr: 10.6.131.78 // # Cop_wait: Avg_time: 10ms P90_time: 20ms Max_time: 30ms Max_Addr: 10.6.131.79 // # Memory_max: 4096 +// # Disk_max: 65535 // # Succ: true // # Prev_stmt: begin; // select * from t_slim; @@ -1683,6 +1687,9 @@ func (s *SessionVars) SlowLogFormat(logItems *SlowQueryLogItems) string { if logItems.MemMax > 0 { writeSlowLogItem(&buf, SlowLogMemMax, strconv.FormatInt(logItems.MemMax, 10)) } + if logItems.DiskMax > 0 { + writeSlowLogItem(&buf, SlowLogDiskMax, strconv.FormatInt(logItems.DiskMax, 10)) + } writeSlowLogItem(&buf, SlowLogPrepared, strconv.FormatBool(logItems.Prepared)) writeSlowLogItem(&buf, SlowLogPlanFromCache, strconv.FormatBool(logItems.PlanFromCache)) diff --git a/sessionctx/variable/session_test.go b/sessionctx/variable/session_test.go index 156881b611ce9..b21ca69270f4e 100644 --- a/sessionctx/variable/session_test.go +++ b/sessionctx/variable/session_test.go @@ -173,6 +173,7 @@ func (*testSessionSuite) TestSlowLogFormat(c *C) { } var memMax int64 = 2333 + var diskMax int64 = 6666 resultString := `# Txn_start_ts: 406649736972468225 # User: root@192.168.0.1 # Conn_ID: 1 @@ -192,6 +193,7 @@ func (*testSessionSuite) TestSlowLogFormat(c *C) { # Cop_backoff_rpcPD_total_times: 200 Cop_backoff_rpcPD_total_time: 0.2 Cop_backoff_rpcPD_max_time: 0.2 Cop_backoff_rpcPD_max_addr: 127.0.0.1 Cop_backoff_rpcPD_avg_time: 0.2 Cop_backoff_rpcPD_p90_time: 0.2 # Cop_backoff_rpcTiKV_total_times: 200 Cop_backoff_rpcTiKV_total_time: 0.2 Cop_backoff_rpcTiKV_max_time: 0.2 Cop_backoff_rpcTiKV_max_addr: 127.0.0.1 Cop_backoff_rpcTiKV_avg_time: 0.2 Cop_backoff_rpcTiKV_p90_time: 0.2 # Mem_max: 2333 +# Disk_max: 6666 # Prepared: true # Plan_from_cache: true # Has_more_results: true @@ -211,6 +213,7 @@ select * from t;` CopTasks: copTasks, ExecDetail: execDetail, MemMax: memMax, + DiskMax: diskMax, Prepared: true, PlanFromCache: true, HasMoreResults: true, diff --git a/util/stmtsummary/statement_summary.go b/util/stmtsummary/statement_summary.go index 98f07ca3becd1..54feab79d67ea 100644 --- a/util/stmtsummary/statement_summary.go +++ b/util/stmtsummary/statement_summary.go @@ -163,6 +163,8 @@ type stmtSummaryByDigestElement struct { // other sumMem int64 maxMem int64 + sumDisk int64 + maxDisk int64 sumAffectedRows uint64 // The first time this type of SQL executes. firstSeen time.Time @@ -192,6 +194,7 @@ type StmtExecInfo struct { CopTasks *stmtctx.CopTasksDetails ExecDetail *execdetails.ExecDetails MemMax int64 + DiskMax int64 StartTime time.Time IsInternal bool Succeed bool @@ -739,6 +742,10 @@ func (ssElement *stmtSummaryByDigestElement) add(sei *StmtExecInfo, intervalSeco if sei.MemMax > ssElement.maxMem { ssElement.maxMem = sei.MemMax } + ssElement.sumDisk += sei.DiskMax + if sei.DiskMax > ssElement.maxDisk { + ssElement.maxDisk = sei.DiskMax + } if sei.StartTime.Before(ssElement.firstSeen) { ssElement.firstSeen = sei.StartTime } @@ -824,6 +831,8 @@ func (ssElement *stmtSummaryByDigestElement) toDatum(ssbd *stmtSummaryByDigest) formatBackoffTypes(ssElement.backoffTypes), avgInt(ssElement.sumMem, ssElement.execCount), ssElement.maxMem, + avgInt(ssElement.sumDisk, ssElement.execCount), + ssElement.maxDisk, avgFloat(int64(ssElement.sumAffectedRows), ssElement.execCount), types.NewTime(types.FromGoTime(ssElement.firstSeen), mysql.TypeTimestamp, 0), types.NewTime(types.FromGoTime(ssElement.lastSeen), mysql.TypeTimestamp, 0), diff --git a/util/stmtsummary/statement_summary_test.go b/util/stmtsummary/statement_summary_test.go index 5dde31d1d1308..fe547212bd120 100644 --- a/util/stmtsummary/statement_summary_test.go +++ b/util/stmtsummary/statement_summary_test.go @@ -127,6 +127,8 @@ func (s *testStmtSummarySuite) TestAddStatement(c *C) { backoffTypes: make(map[fmt.Stringer]int), sumMem: stmtExecInfo1.MemMax, maxMem: stmtExecInfo1.MemMax, + sumDisk: stmtExecInfo1.DiskMax, + maxDisk: stmtExecInfo1.DiskMax, sumAffectedRows: stmtExecInfo1.StmtCtx.AffectedRows(), firstSeen: stmtExecInfo1.StartTime, lastSeen: stmtExecInfo1.StartTime, @@ -204,6 +206,7 @@ func (s *testStmtSummarySuite) TestAddStatement(c *C) { IndexNames: indexes, }, MemMax: 20000, + DiskMax: 20000, StartTime: time.Date(2019, 1, 1, 10, 10, 20, 10, time.UTC), Succeed: true, } @@ -254,6 +257,8 @@ func (s *testStmtSummarySuite) TestAddStatement(c *C) { expectedSummaryElement.backoffTypes[tikv.BoTxnLock] = 1 expectedSummaryElement.sumMem += stmtExecInfo2.MemMax expectedSummaryElement.maxMem = stmtExecInfo2.MemMax + expectedSummaryElement.sumDisk += stmtExecInfo2.DiskMax + expectedSummaryElement.maxDisk = stmtExecInfo2.DiskMax expectedSummaryElement.sumAffectedRows += stmtExecInfo2.StmtCtx.AffectedRows() expectedSummaryElement.lastSeen = stmtExecInfo2.StartTime @@ -319,6 +324,7 @@ func (s *testStmtSummarySuite) TestAddStatement(c *C) { IndexNames: indexes, }, MemMax: 200, + DiskMax: 200, StartTime: time.Date(2019, 1, 1, 10, 10, 0, 10, time.UTC), Succeed: true, } @@ -347,6 +353,7 @@ func (s *testStmtSummarySuite) TestAddStatement(c *C) { expectedSummaryElement.sumBackoffTimes += 1 expectedSummaryElement.backoffTypes[tikv.BoTxnLock] = 2 expectedSummaryElement.sumMem += stmtExecInfo3.MemMax + expectedSummaryElement.sumDisk += stmtExecInfo3.DiskMax expectedSummaryElement.sumAffectedRows += stmtExecInfo3.StmtCtx.AffectedRows() expectedSummaryElement.firstSeen = stmtExecInfo3.StartTime @@ -564,6 +571,7 @@ func generateAnyExecInfo() *StmtExecInfo { IndexNames: indexes, }, MemMax: 10000, + DiskMax: 10000, StartTime: time.Date(2019, 1, 1, 10, 10, 10, 10, time.UTC), Succeed: true, } @@ -605,10 +613,9 @@ func (s *testStmtSummarySuite) TestToDatum(c *C) { stmtExecInfo1.ExecDetail.CommitDetail.WriteSize, stmtExecInfo1.ExecDetail.CommitDetail.WriteSize, stmtExecInfo1.ExecDetail.CommitDetail.PrewriteRegionNum, stmtExecInfo1.ExecDetail.CommitDetail.PrewriteRegionNum, stmtExecInfo1.ExecDetail.CommitDetail.TxnRetry, stmtExecInfo1.ExecDetail.CommitDetail.TxnRetry, 1, - "txnLock:1", stmtExecInfo1.MemMax, stmtExecInfo1.MemMax, stmtExecInfo1.StmtCtx.AffectedRows(), + "txnLock:1", stmtExecInfo1.MemMax, stmtExecInfo1.MemMax, stmtExecInfo1.DiskMax, stmtExecInfo1.DiskMax, stmtExecInfo1.StmtCtx.AffectedRows(), t, t, 0, 0, stmtExecInfo1.OriginalSQL, stmtExecInfo1.PrevSQL, "plan_digest", ""} match(c, datums[0], expectedDatum...) - datums = s.ssMap.ToHistoryDatum(nil, true) c.Assert(len(datums), Equals, 1) match(c, datums[0], expectedDatum...) From e92f15d6ecddd33bda477604707dd7773fb87ff9 Mon Sep 17 00:00:00 2001 From: baishen Date: Tue, 26 May 2020 13:22:40 -0500 Subject: [PATCH 51/74] planner: Add explain info for InspectionSummaryTableExtractor (#16216) --- executor/explainfor_test.go | 53 ++++++++++++++++++++ planner/core/memtable_predicate_extractor.go | 29 ++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/executor/explainfor_test.go b/executor/explainfor_test.go index 4a9bb14eabe98..a7298cdebd72f 100644 --- a/executor/explainfor_test.go +++ b/executor/explainfor_test.go @@ -203,3 +203,56 @@ func (s *testPrepareSerialSuite) TestExplainDotForExplainPlan(c *C) { tk.MustQuery(fmt.Sprintf("explain format=\"dot\" for connection %s", connID)).Check(nil) } + +func (s *testSuite) TestInspectionSummaryTable(c *C) { + tk := testkit.NewTestKitWithInit(c, s.store) + + tk.MustQuery("desc select * from information_schema.inspection_summary where rule='ddl'").Check(testkit.Rows( + `Selection_5 8000.00 root eq(Column#1, "ddl")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["ddl"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where 'ddl'=rule or rule='config'").Check(testkit.Rows( + `Selection_5 8000.00 root or(eq("ddl", Column#1), eq(Column#1, "config"))`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["config","ddl"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where 'ddl'=rule or rule='config' or rule='slow_query'").Check(testkit.Rows( + `Selection_5 8000.00 root or(eq("ddl", Column#1), or(eq(Column#1, "config"), eq(Column#1, "slow_query")))`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["config","ddl","slow_query"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where (rule='config' or rule='slow_query') and (metrics_name='metric_name3' or metrics_name='metric_name1')").Check(testkit.Rows( + `Selection_5 8000.00 root or(eq(Column#1, "config"), eq(Column#1, "slow_query")), or(eq(Column#3, "metric_name3"), eq(Column#3, "metric_name1"))`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["config","slow_query"], metric_names:["metric_name1","metric_name3"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where rule in ('ddl', 'slow_query')").Check(testkit.Rows( + `Selection_5 8000.00 root in(Column#1, "ddl", "slow_query")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["ddl","slow_query"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where rule in ('ddl', 'slow_query') and metrics_name='metric_name1'").Check(testkit.Rows( + `Selection_5 8000.00 root eq(Column#3, "metric_name1"), in(Column#1, "ddl", "slow_query")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["ddl","slow_query"], metric_names:["metric_name1"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where rule in ('ddl', 'slow_query') and metrics_name in ('metric_name1', 'metric_name2')").Check(testkit.Rows( + `Selection_5 8000.00 root in(Column#1, "ddl", "slow_query"), in(Column#3, "metric_name1", "metric_name2")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["ddl","slow_query"], metric_names:["metric_name1","metric_name2"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where rule='ddl' and metrics_name in ('metric_name1', 'metric_name2')").Check(testkit.Rows( + `Selection_5 8000.00 root eq(Column#1, "ddl"), in(Column#3, "metric_name1", "metric_name2")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["ddl"], metric_names:["metric_name1","metric_name2"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where rule='ddl' and metrics_name='metric_NAME3'").Check(testkit.Rows( + `Selection_5 8000.00 root eq(Column#1, "ddl"), eq(Column#3, "metric_NAME3")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["ddl"], metric_names:["metric_name3"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where rule in ('ddl', 'config') and rule in ('slow_query', 'config')").Check(testkit.Rows( + `Selection_5 8000.00 root in(Column#1, "ddl", "config"), in(Column#1, "slow_query", "config")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["config"]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where metrics_name in ('metric_name1', 'metric_name4') and metrics_name in ('metric_name5', 'metric_name4') and rule in ('ddl', 'config') and rule in ('slow_query', 'config') and quantile in (0.80, 0.90)").Check(testkit.Rows( + `Selection_5 8000.00 root in(Column#1, "ddl", "config"), in(Column#1, "slow_query", "config"), in(Column#3, "metric_name1", "metric_name4"), in(Column#3, "metric_name5", "metric_name4")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY rules:["config"], metric_names:["metric_name4"], quantiles:[0.800000,0.900000]`, + )) + tk.MustQuery("desc select * from information_schema.inspection_summary where metrics_name in ('metric_name1', 'metric_name4') and metrics_name in ('metric_name5', 'metric_name4') and metrics_name in ('metric_name5', 'metric_name1') and metrics_name in ('metric_name1', 'metric_name3')").Check(testkit.Rows( + `Selection_5 8000.00 root in(Column#3, "metric_name1", "metric_name3"), in(Column#3, "metric_name1", "metric_name4"), in(Column#3, "metric_name5", "metric_name1"), in(Column#3, "metric_name5", "metric_name4")`, + `└─MemTableScan_6 10000.00 root table:INSPECTION_SUMMARY skip_inspection: true`, + )) +} diff --git a/planner/core/memtable_predicate_extractor.go b/planner/core/memtable_predicate_extractor.go index 9d810cc6c2893..160613d01b027 100644 --- a/planner/core/memtable_predicate_extractor.go +++ b/planner/core/memtable_predicate_extractor.go @@ -861,7 +861,34 @@ func (e *InspectionSummaryTableExtractor) Extract( } func (e *InspectionSummaryTableExtractor) explainInfo(p *PhysicalMemTable) string { - return "" + if e.SkipInspection { + return "skip_inspection: true" + } + + r := new(bytes.Buffer) + if len(e.Rules) > 0 { + r.WriteString(fmt.Sprintf("rules:[%s], ", extractStringFromStringSet(e.Rules))) + } + if len(e.MetricNames) > 0 { + r.WriteString(fmt.Sprintf("metric_names:[%s], ", extractStringFromStringSet(e.MetricNames))) + } + if len(e.Quantiles) > 0 { + r.WriteString("quantiles:[") + for i, quantile := range e.Quantiles { + if i > 0 { + r.WriteByte(',') + } + r.WriteString(fmt.Sprintf("%f", quantile)) + } + r.WriteString("], ") + } + + // remove the last ", " in the message info + s := r.String() + if len(s) > 2 { + return s[:len(s)-2] + } + return s } // InspectionRuleTableExtractor is used to extract some predicates of `inspection_rules` From 7ca3d9cadccaa19b12e20c2b5d71615c7e735a5d Mon Sep 17 00:00:00 2001 From: lawyerphx Date: Wed, 27 May 2020 09:56:40 +0800 Subject: [PATCH 52/74] bindinfo, record how bindings are created in SQL bindings. (#17254) --- bindinfo/bind_test.go | 60 +++++++++++++++++++++++++++++++++++-- bindinfo/cache.go | 7 +++++ bindinfo/handle.go | 7 +++-- executor/bind.go | 1 + executor/show.go | 1 + infoschema/tables_test.go | 2 +- planner/core/planbuilder.go | 4 +-- planner/optimize.go | 1 + session/bootstrap.go | 11 +++++++ session/session.go | 2 +- 10 files changed, 87 insertions(+), 9 deletions(-) diff --git a/bindinfo/bind_test.go b/bindinfo/bind_test.go index 353d166448da4..bd4f02ada88a0 100644 --- a/bindinfo/bind_test.go +++ b/bindinfo/bind_test.go @@ -118,8 +118,9 @@ func (s *testSuite) TestBindParse(c *C) { status := "using" charset := "utf8mb4" collation := "utf8mb4_bin" - sql := fmt.Sprintf(`INSERT INTO mysql.bind_info(original_sql,bind_sql,default_db,status,create_time,update_time,charset,collation) VALUES ('%s', '%s', '%s', '%s', NOW(), NOW(),'%s', '%s')`, - originSQL, bindSQL, defaultDb, status, charset, collation) + source := bindinfo.Manual + sql := fmt.Sprintf(`INSERT INTO mysql.bind_info(original_sql,bind_sql,default_db,status,create_time,update_time,charset,collation,source) VALUES ('%s', '%s', '%s', '%s', NOW(), NOW(),'%s', '%s', '%s')`, + originSQL, bindSQL, defaultDb, status, charset, collation, source) tk.MustExec(sql) bindHandle := bindinfo.NewBindHandle(tk.Se) err := bindHandle.Update(true) @@ -858,7 +859,8 @@ func (s *testSuite) TestEvolveInvalidBindings(c *C) { tk.MustExec("create table t(a int, b int, index idx_a(a))") tk.MustExec("create global binding for select * from t where a > 10 using select /*+ USE_INDEX(t) */ * from t where a > 10") // Manufacture a rejected binding by hacking mysql.bind_info. - tk.MustExec("insert into mysql.bind_info values('select * from t where a > ?', 'select /*+ USE_INDEX(t,idx_a) */ * from t where a > 10', 'test', 'rejected', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '')") + tk.MustExec("insert into mysql.bind_info values('select * from t where a > ?', 'select /*+ USE_INDEX(t,idx_a) */ * from t where a > 10', 'test', 'rejected', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + + bindinfo.Manual + "')") tk.MustQuery("select bind_sql, status from mysql.bind_info").Sort().Check(testkit.Rows( "select /*+ USE_INDEX(t) */ * from t where a > 10 using", "select /*+ USE_INDEX(t,idx_a) */ * from t where a > 10 rejected", @@ -1165,3 +1167,55 @@ func (s *testSuite) TestInvisibleIndex(c *C) { tk.MustExec("drop binding for select * from t") } + +func (s *testSuite) TestbindingSource(c *C) { + tk := testkit.NewTestKit(c, s.store) + s.cleanBindingEnv(tk) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, index idx_a(a))") + + // Test Source for SQL created sql + tk.MustExec("create global binding for select * from t where a > 10 using select * from t ignore index(idx_a) where a > 10") + bindHandle := s.domain.BindHandle() + sql, hash := parser.NormalizeDigest("select * from t where a > ?") + bindData := bindHandle.GetBindRecord(hash, sql, "test") + c.Check(bindData, NotNil) + c.Check(bindData.OriginalSQL, Equals, "select * from t where a > ?") + c.Assert(len(bindData.Bindings), Equals, 1) + bind := bindData.Bindings[0] + c.Assert(bind.Source, Equals, bindinfo.Manual) + + // Test Source for evolved sql + tk.MustExec("set @@tidb_evolve_plan_baselines=1") + tk.MustQuery("select * from t where a > 10") + bindHandle.SaveEvolveTasksToStore() + sql, hash = parser.NormalizeDigest("select * from t where a > ?") + bindData = bindHandle.GetBindRecord(hash, sql, "test") + c.Check(bindData, NotNil) + c.Check(bindData.OriginalSQL, Equals, "select * from t where a > ?") + c.Assert(len(bindData.Bindings), Equals, 2) + bind = bindData.Bindings[1] + c.Assert(bind.Source, Equals, bindinfo.Evolve) + tk.MustExec("set @@tidb_evolve_plan_baselines=0") + + // Test Source for captured sqls + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("set @@tidb_capture_plan_baselines = on") + defer func() { + tk.MustExec("set @@tidb_capture_plan_baselines = off") + }() + tk.MustExec("use test") + c.Assert(tk.Se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil), IsTrue) + tk.MustExec("select * from t ignore index(idx_a) where a < 10") + tk.MustExec("select * from t ignore index(idx_a) where a < 10") + tk.MustExec("admin capture bindings") + bindHandle.CaptureBaselines() + sql, hash = parser.NormalizeDigest("select * from t where a < ?") + bindData = bindHandle.GetBindRecord(hash, sql, "test") + c.Check(bindData, NotNil) + c.Check(bindData.OriginalSQL, Equals, "select * from t where a < ?") + c.Assert(len(bindData.Bindings), Equals, 1) + bind = bindData.Bindings[0] + c.Assert(bind.Source, Equals, bindinfo.Capture) +} diff --git a/bindinfo/cache.go b/bindinfo/cache.go index 7fd44b934e892..f4af01b12e5f7 100644 --- a/bindinfo/cache.go +++ b/bindinfo/cache.go @@ -36,6 +36,12 @@ const ( // Rejected means that the bind has been rejected after verify process. // We can retry it after certain time has passed. Rejected = "rejected" + // Manual indicates the binding is created by SQL like "create binding for ...". + Manual = "manual" + // Capture indicates the binding is captured by TiDB automatically. + Capture = "capture" + // Evolve indicates the binding is evolved by TiDB from old bindings. + Evolve = "evolve" ) // Binding stores the basic bind hint info. @@ -47,6 +53,7 @@ type Binding struct { Status string CreateTime types.Time UpdateTime types.Time + Source string Charset string Collation string // Hint is the parsed hints, it is used to bind hints to stmt node. diff --git a/bindinfo/handle.go b/bindinfo/handle.go index e10d9fe212a38..3d113029ed95a 100644 --- a/bindinfo/handle.go +++ b/bindinfo/handle.go @@ -125,7 +125,7 @@ func (h *BindHandle) Update(fullLoad bool) (err error) { lastUpdateTime := h.bindInfo.lastUpdateTime h.bindInfo.Unlock() - sql := "select original_sql, bind_sql, default_db, status, create_time, update_time, charset, collation from mysql.bind_info" + sql := "select original_sql, bind_sql, default_db, status, create_time, update_time, charset, collation, source from mysql.bind_info" if !fullLoad { sql += " where update_time > \"" + lastUpdateTime.String() + "\"" } @@ -462,6 +462,7 @@ func (h *BindHandle) newBindRecord(row chunk.Row) (string, *BindRecord, error) { UpdateTime: row.GetTime(5), Charset: row.GetString(6), Collation: row.GetString(7), + Source: row.GetString(8), } bindRecord := &BindRecord{ OriginalSQL: row.GetString(0), @@ -567,7 +568,7 @@ func (h *BindHandle) deleteBindInfoSQL(normdOrigSQL, db, bindSQL string) string } func (h *BindHandle) insertBindInfoSQL(orignalSQL string, db string, info Binding) string { - return fmt.Sprintf(`INSERT INTO mysql.bind_info VALUES (%s, %s, %s, %s, %s, %s, %s, %s)`, + return fmt.Sprintf(`INSERT INTO mysql.bind_info VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)`, expression.Quote(orignalSQL), expression.Quote(info.BindSQL), expression.Quote(db), @@ -576,6 +577,7 @@ func (h *BindHandle) insertBindInfoSQL(orignalSQL string, db string, info Bindin expression.Quote(info.UpdateTime.String()), expression.Quote(info.Charset), expression.Quote(info.Collation), + expression.Quote(info.Source), ) } @@ -628,6 +630,7 @@ func (h *BindHandle) CaptureBaselines() { Status: Using, Charset: charset, Collation: collation, + Source: Capture, } // We don't need to pass the `sctx` because the BindSQL has been validated already. err = h.AddBindRecord(nil, &BindRecord{OriginalSQL: normalizedSQL, Db: dbName, Bindings: []Binding{binding}}) diff --git a/executor/bind.go b/executor/bind.go index e4c84d0dcb0d6..4b66d46415316 100644 --- a/executor/bind.go +++ b/executor/bind.go @@ -82,6 +82,7 @@ func (e *SQLBindExec) createSQLBind() error { Charset: e.charset, Collation: e.collation, Status: bindinfo.Using, + Source: bindinfo.Manual, } record := &bindinfo.BindRecord{ OriginalSQL: e.normdOrigSQL, diff --git a/executor/show.go b/executor/show.go index 76307c76476b4..78186f6184ccd 100644 --- a/executor/show.go +++ b/executor/show.go @@ -276,6 +276,7 @@ func (e *ShowExec) fetchShowBind() error { hint.UpdateTime, hint.Charset, hint.Collation, + hint.Source, }) } } diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index 0cf81a222dd0f..116de36a572dc 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -1217,7 +1217,7 @@ func (s *testTableSuite) TestStmtSummaryInternalQuery(c *C) { tk.MustQuery(`select exec_count, digest_text from information_schema.statements_summary where digest_text like "select original_sql , bind_sql , default_db , status%"`).Check(testkit.Rows( - "1 select original_sql , bind_sql , default_db , status , create_time , update_time , charset , collation from mysql . bind_info" + + "1 select original_sql , bind_sql , default_db , status , create_time , update_time , charset , collation , source from mysql . bind_info" + " where update_time > ? order by update_time")) } diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 00fa69f4115e7..2a07727e5db11 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -3107,8 +3107,8 @@ func buildShowSchema(s *ast.ShowStmt, isView bool, isSequence bool) (schema *exp names = []string{"Privilege", "Context", "Comment"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} case ast.ShowBindings: - names = []string{"Original_sql", "Bind_sql", "Default_db", "Status", "Create_time", "Update_time", "Charset", "Collation"} - ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeDatetime, mysql.TypeDatetime, mysql.TypeVarchar, mysql.TypeVarchar} + names = []string{"Original_sql", "Bind_sql", "Default_db", "Status", "Create_time", "Update_time", "Charset", "Collation", "Source"} + ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeDatetime, mysql.TypeDatetime, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar} case ast.ShowAnalyzeStatus: names = []string{"Table_schema", "Table_name", "Partition_name", "Job_info", "Processed_rows", "Start_time", "State"} ftypes = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, mysql.TypeDatetime, mysql.TypeVarchar} diff --git a/planner/optimize.go b/planner/optimize.go index da469643a1dad..cb097abd26266 100644 --- a/planner/optimize.go +++ b/planner/optimize.go @@ -266,6 +266,7 @@ func handleEvolveTasks(ctx context.Context, sctx sessionctx.Context, br *bindinf Status: bindinfo.PendingVerify, Charset: charset, Collation: collation, + Source: bindinfo.Evolve, } globalHandle := domain.GetDomain(sctx).BindHandle() globalHandle.AddEvolvePlanTask(br.OriginalSQL, br.Db, binding) diff --git a/session/bootstrap.go b/session/bootstrap.go index b32e7de3b4be8..d9d0d9faa5cc7 100644 --- a/session/bootstrap.go +++ b/session/bootstrap.go @@ -240,6 +240,7 @@ const ( update_time timestamp(3) NOT NULL, charset text NOT NULL, collation text NOT NULL, + source varchar(10) NOT NULL default 'unknown', INDEX sql_index(original_sql(1024),default_db(1024)) COMMENT "accelerate the speed when add global binding query", INDEX time_index(update_time) COMMENT "accelerate the speed when querying with last update time" ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` @@ -386,6 +387,8 @@ const ( version45 = 45 // version46 fix a bug in v3.1.1. version46 = 46 + // version47 add Source to bindings to indicate the way binding created. + version47 = 47 ) var ( @@ -435,6 +438,7 @@ var ( upgradeToVer44, upgradeToVer45, upgradeToVer46, + upgradeToVer47, } ) @@ -1060,6 +1064,13 @@ func upgradeToVer46(s Session, ver int64) { mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET File_priv='Y' where Super_priv='Y'") } +func upgradeToVer47(s Session, ver int64) { + if ver >= version47 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD COLUMN `source` varchar(10) NOT NULL default 'unknown'", infoschema.ErrColumnExists) +} + // updateBootstrapVer updates bootstrap version variable in mysql.TiDB table. func updateBootstrapVer(s Session) { // Update bootstrap version. diff --git a/session/session.go b/session/session.go index de5f7e5f4fbe1..4f62d71adaaac 100644 --- a/session/session.go +++ b/session/session.go @@ -1920,7 +1920,7 @@ func CreateSessionWithDomain(store kv.Storage, dom *domain.Domain) (*session, er const ( notBootstrapped = 0 - currentBootstrapVersion = version46 + currentBootstrapVersion = version47 ) func getStoreBootstrapVersion(store kv.Storage) int64 { From 572bba0499e1f6341eec53f542212a348d33fb44 Mon Sep 17 00:00:00 2001 From: vincent178 Date: Wed, 27 May 2020 11:04:57 +0800 Subject: [PATCH 53/74] *: Update Apache Arrow memory layout document link (#17047) --- util/chunk/chunk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/chunk/chunk.go b/util/chunk/chunk.go index b6244029cbd5a..3312ecf040916 100644 --- a/util/chunk/chunk.go +++ b/util/chunk/chunk.go @@ -26,7 +26,7 @@ import ( var msgErrSelNotNil = "The selection vector of Chunk is not nil. Please file a bug to the TiDB Team" // Chunk stores multiple rows of data in Apache Arrow format. -// See https://arrow.apache.org/docs/memory_layout.html +// See https://arrow.apache.org/docs/format/Columnar.html#physical-memory-layout // Values are appended in compact format and can be directly accessed without decoding. // When the chunk is done processing, we can reuse the allocated memory by resetting it. type Chunk struct { From 322c55a82a6bce2fe7f07047cc170993e1c1b3d6 Mon Sep 17 00:00:00 2001 From: lysu Date: Wed, 27 May 2020 13:55:10 +0800 Subject: [PATCH 54/74] tikv: fix infinite follower/learner retry when network partition only between leader and follower/learner (#17441) --- store/tikv/region_cache.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/store/tikv/region_cache.go b/store/tikv/region_cache.go index edf875db735e4..46fd7b38c89ee 100644 --- a/store/tikv/region_cache.go +++ b/store/tikv/region_cache.go @@ -502,9 +502,11 @@ func (c *RegionCache) OnSendFail(bo *Backoffer, ctx *RPCContext, scheduleReload rs := r.getStore() if err != nil { s := rs.stores[ctx.PeerIdx] + followerRead := int(rs.workTiKVIdx) != ctx.PeerIdx - // send fail but store is reachable, keep retry current peer. - if s.requestLiveness(bo) == reachable { + // send fail but store is reachable, keep retry current peer for replica leader request. + // but we still need switch peer for follower-read or learner-read(i.e. tiflash) + if ctx.Store.storeType == kv.TiKV && !followerRead && s.requestLiveness(bo) == reachable { return } From af7bbbe2412f9a0174338526daa01fe270500806 Mon Sep 17 00:00:00 2001 From: "Zhuomin(Charming) Liu" Date: Wed, 27 May 2020 20:47:11 +0800 Subject: [PATCH 55/74] executor: fix failpoint race for analyze test (#17437) Co-authored-by: pingcap-github-bot --- executor/analyze_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/executor/analyze_test.go b/executor/analyze_test.go index 8d8eef42b8e89..16743396d600e 100644 --- a/executor/analyze_test.go +++ b/executor/analyze_test.go @@ -518,7 +518,7 @@ func (s *testFastAnalyze) TestFastAnalyzeRetryRowCount(c *C) { c.Assert(row[5], Equals, "30") } -func (s *testSuite1) TestFailedAnalyzeRequest(c *C) { +func (s *testSuite9) TestFailedAnalyzeRequest(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") tk.MustExec("drop table if exists t") From 12bd862a466581be79c5c9b4599c4041ce71f543 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Thu, 28 May 2020 14:58:03 +0800 Subject: [PATCH 56/74] ddl,expression: fix using extract() function as partition key when creating partition table (#17461) --- ddl/db_partition_test.go | 7 +++++++ ddl/partition.go | 19 +++++++++++++------ expression/simple_rewriter.go | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/ddl/db_partition_test.go b/ddl/db_partition_test.go index 1d2288ca35f00..b8806365c7bd3 100644 --- a/ddl/db_partition_test.go +++ b/ddl/db_partition_test.go @@ -289,6 +289,10 @@ partition by range (a) tk.MustGetErrCode("alter table too_long_identifier add partition "+ "(partition p0pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp values less than(20))", tmysql.ErrTooLongIdent) + tk.MustExec(`create table t36 (a date, b datetime) partition by range (EXTRACT(YEAR_MONTH FROM a)) ( + partition p0 values less than (200), + partition p1 values less than (300), + partition p2 values less than maxvalue)`) } func (s *testIntegrationSuite2) TestCreateTableWithHashPartition(c *C) { @@ -341,6 +345,9 @@ func (s *testIntegrationSuite2) TestCreateTableWithHashPartition(c *C) { PARTITION p1 VALUES LESS THAN (200), PARTITION p2 VALUES LESS THAN MAXVALUE)`) tk.MustGetErrCode("select * from t_sub partition (p0)", tmysql.ErrPartitionClauseOnNonpartitioned) + + // Fix create partition table using extract() function as partition key. + tk.MustExec("create table t2 (a date, b datetime) partition by hash (EXTRACT(YEAR_MONTH FROM a)) partitions 7") } func (s *testIntegrationSuite1) TestCreateTableWithRangeColumnPartition(c *C) { diff --git a/ddl/partition.go b/ddl/partition.go index 601760b234c25..5b09a0b4e0b09 100644 --- a/ddl/partition.go +++ b/ddl/partition.go @@ -421,8 +421,12 @@ func checkResultOK(ok bool, err error) error { } func checkPartitionColumns(tblInfo *model.TableInfo, expr ast.ExprNode) ([]*model.ColumnInfo, error) { - buf := new(bytes.Buffer) - expr.Format(buf) + var buf strings.Builder + restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &buf) + err := expr.Restore(restoreCtx) + if err != nil { + return nil, errors.Trace(err) + } partCols, err := extractPartitionColumns(buf.String(), tblInfo) if err != nil { return nil, err @@ -440,8 +444,11 @@ func checkPartitionFuncType(ctx sessionctx.Context, s *ast.CreateTableStmt, tblI if s.Partition.Expr == nil { return nil } - buf := new(bytes.Buffer) - s.Partition.Expr.Format(buf) + var buf strings.Builder + restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &buf) + if err := s.Partition.Expr.Restore(restoreCtx); err != nil { + return errors.Trace(err) + } exprStr := buf.String() if s.Partition.Tp == model.PartitionTypeRange || s.Partition.Tp == model.PartitionTypeHash { // if partition by columnExpr, check the column type @@ -841,7 +848,7 @@ func extractPartitionColumns(partExpr string, tblInfo *model.TableInfo) ([]*mode partExpr = "select " + partExpr stmts, _, err := parser.New().Parse(partExpr, "", "") if err != nil { - return nil, err + return nil, errors.Trace(err) } extractor := &columnNameExtractor{ tblInfo: tblInfo, @@ -849,7 +856,7 @@ func extractPartitionColumns(partExpr string, tblInfo *model.TableInfo) ([]*mode } stmts[0].Accept(extractor) if extractor.err != nil { - return nil, extractor.err + return nil, errors.Trace(extractor.err) } return extractor.extractedColumns, nil } diff --git a/expression/simple_rewriter.go b/expression/simple_rewriter.go index 538c63b6d6444..2b16c4abc2cd4 100644 --- a/expression/simple_rewriter.go +++ b/expression/simple_rewriter.go @@ -58,7 +58,7 @@ func ParseSimpleExprWithTableInfo(ctx sessionctx.Context, exprStr string, tableI } if err != nil { - return nil, util.SyntaxError(err) + return nil, errors.Trace(err) } expr := stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr return RewriteSimpleExprWithTableInfo(ctx, tableInfo, expr) From b5bb0090e769a61196d0b7b3257563465593a894 Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Thu, 28 May 2020 09:05:10 +0200 Subject: [PATCH 57/74] *: fix LIKE expressions with _ following % (#17418) --- executor/memtable_reader_test.go | 13 +++++++++++++ expression/builtin_like_test.go | 28 ++++++++++++++++++++++------ util/collate/general_ci.go | 10 ---------- util/stringutil/string_util.go | 20 +------------------- util/stringutil/string_util_test.go | 4 ++-- 5 files changed, 38 insertions(+), 37 deletions(-) diff --git a/executor/memtable_reader_test.go b/executor/memtable_reader_test.go index 830c8ca474382..84bd36cde6cbe 100644 --- a/executor/memtable_reader_test.go +++ b/executor/memtable_reader_test.go @@ -849,6 +849,19 @@ func (s *testMemTableReaderSuite) TestTiDBClusterLog(c *C) { {"2019/08/26 06:27:17.011", "pd", "critical", "[test log message pd 14, bar]"}, }, }, + { + conditions: []string{ + "time>='2019/08/26 06:18:13.011'", + "time<='2099/08/26 06:28:19.011'", + // this pattern verifies that there is no optimization breaking + // length of multiple wildcards, for example, %% may be + // converted to %, but %_ cannot be converted to %. + "message like '%tidb_%_4%'", + }, + expected: [][]string{ + {"2019/08/26 06:25:17.011", "tidb", "critical", "[test log message tidb 14, bar]"}, + }, + }, } var servers []string diff --git a/expression/builtin_like_test.go b/expression/builtin_like_test.go index 8d9310fdb14f9..fbddc60b91e95 100644 --- a/expression/builtin_like_test.go +++ b/expression/builtin_like_test.go @@ -35,15 +35,23 @@ func (s *testEvaluatorSuite) TestLike(c *C) { {"aA", "Aa", 0}, {"aAb", `Aa%`, 0}, {"aAb", "aA_", 1}, + {"baab", "b_%b", 1}, + {"baab", "b%_b", 1}, + {"bab", "b_%b", 1}, + {"bab", "b%_b", 1}, + {"bb", "b_%b", 0}, + {"bb", "b%_b", 0}, + {"baabccc", "b_%b%", 1}, } for _, tt := range tests { + commentf := Commentf(`for input = "%s", pattern = "%s"`, tt.input, tt.pattern) fc := funcs[ast.Like] f, err := fc.getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(tt.input, tt.pattern, 0))) - c.Assert(err, IsNil) + c.Assert(err, IsNil, commentf) r, err := evalBuiltinFuncConcurrent(f, chunk.Row{}) - c.Assert(err, IsNil) - c.Assert(r, testutil.DatumEquals, types.NewDatum(tt.match)) + c.Assert(err, IsNil, commentf) + c.Assert(r, testutil.DatumEquals, types.NewDatum(tt.match), commentf) } } @@ -64,16 +72,24 @@ func (s *testEvaluatorSerialSuites) TestCILike(c *C) { {"áAb", `%ab%`, 1}, {"áAb", `%ab`, 1}, {"ÀAb", "aA_", 1}, + {"áééá", "a_%a", 1}, + {"áééá", "a%_a", 1}, + {"áéá", "a_%a", 1}, + {"áéá", "a%_a", 1}, + {"áá", "a_%a", 0}, + {"áá", "a%_a", 0}, + {"áééáííí", "a_%a%", 1}, } for _, tt := range tests { + commentf := Commentf(`for input = "%s", pattern = "%s"`, tt.input, tt.pattern) fc := funcs[ast.Like] inputs := s.datumsToConstants(types.MakeDatums(tt.input, tt.pattern, 0)) f, err := fc.getFunction(s.ctx, inputs) - c.Assert(err, IsNil) + c.Assert(err, IsNil, commentf) f.setCollator(collate.GetCollator("utf8mb4_general_ci")) r, err := evalBuiltinFunc(f, chunk.Row{}) - c.Assert(err, IsNil) - c.Assert(r, testutil.DatumEquals, types.NewDatum(tt.match)) + c.Assert(err, IsNil, commentf) + c.Assert(r, testutil.DatumEquals, types.NewDatum(tt.match), commentf) } } diff --git a/util/collate/general_ci.go b/util/collate/general_ci.go index 122030b1a46fe..b24a55c4cdfcc 100644 --- a/util/collate/general_ci.go +++ b/util/collate/general_ci.go @@ -34,7 +34,6 @@ func sign(i int) int { // compilePatternGeneralCI handles escapes and wild cards, generate pattern weights and types. // This function is modified from stringutil.CompilePattern. func compilePatternGeneralCI(pattern string, escape byte) (patWeights []uint16, patTypes []byte) { - var lastAny bool runes := []rune(pattern) escapeRune := rune(escape) lenRunes := len(runes) @@ -46,7 +45,6 @@ func compilePatternGeneralCI(pattern string, escape byte) (patWeights []uint16, var r = runes[i] switch r { case escapeRune: - lastAny = false tp = stringutil.PatMatch if i < lenRunes-1 { i++ @@ -65,18 +63,10 @@ func compilePatternGeneralCI(pattern string, escape byte) (patWeights []uint16, } } case '_': - if lastAny { - continue - } tp = stringutil.PatOne case '%': - if lastAny { - continue - } - lastAny = true tp = stringutil.PatAny default: - lastAny = false tp = stringutil.PatMatch } patWeights[patLen] = convertRune(r) diff --git a/util/stringutil/string_util.go b/util/stringutil/string_util.go index 39d4f780b7b7f..c9166ae9a11f4 100644 --- a/util/stringutil/string_util.go +++ b/util/stringutil/string_util.go @@ -139,7 +139,6 @@ const ( // CompilePattern handles escapes and wild cards convert pattern characters and // pattern types. func CompilePattern(pattern string, escape byte) (patChars, patTypes []byte) { - var lastAny bool patChars = make([]byte, len(pattern)) patTypes = make([]byte, len(pattern)) patLen := 0 @@ -148,7 +147,6 @@ func CompilePattern(pattern string, escape byte) (patChars, patTypes []byte) { var c = pattern[i] switch c { case escape: - lastAny = false tp = PatMatch if i < len(pattern)-1 { i++ @@ -167,18 +165,10 @@ func CompilePattern(pattern string, escape byte) (patChars, patTypes []byte) { } } case '_': - if lastAny { - continue - } tp = PatOne case '%': - if lastAny { - continue - } - lastAny = true tp = PatAny default: - lastAny = false tp = PatMatch } patChars[patLen] = c @@ -213,16 +203,8 @@ func CompileLike2Regexp(str string) string { case PatMatch: result = append(result, patChars[i]) case PatOne: - // .*. == .* - if !bytes.HasSuffix(result, []byte{'.', '*'}) { - result = append(result, '.') - } + result = append(result, '.') case PatAny: - // ..* == .* - if bytes.HasSuffix(result, []byte{'.'}) { - result = append(result, '*') - continue - } // .*.* == .* if !bytes.HasSuffix(result, []byte{'.', '*'}) { result = append(result, '.') diff --git a/util/stringutil/string_util_test.go b/util/stringutil/string_util_test.go index 8f9ffa67d938f..d739b420af3ed 100644 --- a/util/stringutil/string_util_test.go +++ b/util/stringutil/string_util_test.go @@ -144,8 +144,8 @@ func (s *testStringUtilSuite) TestCompileLike2Regexp(c *C) { {`\_a`, `_a`}, {`\\_a`, `\.a`}, {`\a\b`, `\a\b`}, - {`%%_`, `.*`}, - {`%_%_aA`, ".*aA"}, + {`%%_`, `.*.`}, + {`%_%_aA`, ".*..*.aA"}, } for _, v := range tbl { result := CompileLike2Regexp(v.pattern) From 18a38c674ec7d0d8f22002867b39028cde81feda Mon Sep 17 00:00:00 2001 From: "Zhuomin(Charming) Liu" Date: Thu, 28 May 2020 16:16:36 +0800 Subject: [PATCH 58/74] executor: fix the forever hang in index merge join when oom occurs (#17435) --- executor/index_lookup_merge_join.go | 19 +++++++++++++++--- executor/index_lookup_merge_join_test.go | 25 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 executor/index_lookup_merge_join_test.go diff --git a/executor/index_lookup_merge_join.go b/executor/index_lookup_merge_join.go index 648bd2cb4e783..b476306712dcf 100644 --- a/executor/index_lookup_merge_join.go +++ b/executor/index_lookup_merge_join.go @@ -16,10 +16,13 @@ package executor import ( "context" "fmt" + "runtime" "sort" "sync" "sync/atomic" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/expression" plannercore "github.com/pingcap/tidb/planner/core" @@ -389,9 +392,14 @@ func (imw *innerMergeWorker) run(ctx context.Context, wg *sync.WaitGroup, cancel defer func() { wg.Done() if r := recover(); r != nil { - logutil.Logger(ctx).Error("panic in innerMergeWorker.run", - zap.Reflect("r", r), - zap.Stack("stack trace")) + if task != nil { + task.doneErr = errors.Errorf("%v", r) + close(task.results) + } + buf := make([]byte, 4096) + stackSize := runtime.Stack(buf, false) + buf = buf[:stackSize] + logutil.Logger(ctx).Error("innerMergeWorker panicked", zap.String("stack", string(buf))) cancelFunc() } }() @@ -436,6 +444,11 @@ func (imw *innerMergeWorker) handleTask(ctx context.Context, task *lookUpMergeJo } } task.memTracker.Consume(int64(cap(task.outerOrderIdx))) + failpoint.Inject("IndexMergeJoinMockOOM", func(val failpoint.Value) { + if val.(bool) { + panic("OOM test index merge join doesn't hang here.") + } + }) // needOuterSort means the outer side property items can't guarantee the order of join keys. // Because the necessary condition of merge join is both outer and inner keep order of join keys. // In this case, we need sort the outer side. diff --git a/executor/index_lookup_merge_join_test.go b/executor/index_lookup_merge_join_test.go new file mode 100644 index 0000000000000..f9336c244447b --- /dev/null +++ b/executor/index_lookup_merge_join_test.go @@ -0,0 +1,25 @@ +package executor_test + +import ( + . "github.com/pingcap/check" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/util/testkit" +) + +func (s *testSuite9) TestIndexLookupMergeJoinHang(c *C) { + c.Assert(failpoint.Enable("github.com/pingcap/tidb/executor/IndexMergeJoinMockOOM", `return(true)`), IsNil) + defer func() { + c.Assert(failpoint.Disable("github.com/pingcap/tidb/executor/IndexMergeJoinMockOOM"), IsNil) + }() + + tk := testkit.NewTestKitWithInit(c, s.store) + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (a int,b int,index idx(a))") + tk.MustExec("create table t2 (a int,b int,index idx(a))") + tk.MustExec("insert into t1 values (1,1),(2,2),(3,3),(2000,2000)") + tk.MustExec("insert into t2 values (1,1),(2,2),(3,3),(2000,2000)") + // Do not hang in index merge join when OOM occurs. + err := tk.QueryToErr("select /*+ INL_MERGE_JOIN(t1, t2) */ * from t1, t2 where t1.a = t2.a") + c.Assert(err, NotNil) + c.Assert(err.Error(), Equals, "OOM test index merge join doesn't hang here.") +} From 959ad888b01196982c2bffb37d0e400532d90e3b Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Thu, 28 May 2020 11:37:13 +0200 Subject: [PATCH 59/74] types: fix StrToDate handling of %h and %p (#17395) --- types/format_test.go | 5 +++++ types/time.go | 11 +---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/types/format_test.go b/types/format_test.go index c6c206cc806d3..17e15c7c4ee2b 100644 --- a/types/format_test.go +++ b/types/format_test.go @@ -103,6 +103,8 @@ func (s *testTimeSuite) TestStrToDate(c *C) { {`10:13 PM`, `%l:%i %p`, types.FromDate(0, 0, 0, 22, 13, 0, 0)}, {`12:00:00 AM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 0, 0, 0, 0)}, {`12:00:00 PM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 12, 0, 0, 0)}, + {`12:00:00 PM`, `%I:%i:%s %p`, types.FromDate(0, 0, 0, 12, 0, 0, 0)}, + {`1:00:00 PM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 13, 0, 0, 0)}, {`18/10/22`, `%y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, {`8/10/22`, `%y/%m/%d`, types.FromDate(2008, 10, 22, 0, 0, 0, 0)}, {`69/10/22`, `%y/%m/%d`, types.FromDate(2069, 10, 22, 0, 0, 0, 0)}, @@ -133,6 +135,9 @@ func (s *testTimeSuite) TestStrToDate(c *C) { {`18`, `%l`}, {`00:21:22 AM`, `%h:%i:%s %p`}, {`100/10/22`, `%y/%m/%d`}, + {"2010-11-12 11 am", `%Y-%m-%d %H %p`}, + {"2010-11-12 13 am", `%Y-%m-%d %h %p`}, + {"2010-11-12 0 am", `%Y-%m-%d %h %p`}, } for _, tt := range errTests { var t types.Time diff --git a/types/time.go b/types/time.go index e2d691904272f..8674ab4c505cb 100644 --- a/types/time.go +++ b/types/time.go @@ -2567,7 +2567,7 @@ var dateFormatParserTable = map[string]dateFormatParser{ "%d": dayOfMonthNumeric, // Day of the month, numeric (0..31) "%e": dayOfMonthNumeric, // Day of the month, numeric (0..31) "%f": microSeconds, // Microseconds (000000..999999) - "%h": hour24TwoDigits, // Hour (01..12) + "%h": hour12Numeric, // Hour (01..12) "%H": hour24Numeric, // Hour (00..23) "%I": hour12Numeric, // Hour (01..12) "%i": minutesNumeric, // Minutes, numeric (00..59) @@ -2649,15 +2649,6 @@ func parseDigits(input string, count int) (int, bool) { return int(v), true } -func hour24TwoDigits(t *CoreTime, input string, ctx map[string]int) (string, bool) { - v, succ := parseDigits(input, 2) - if !succ || v >= 24 { - return input, false - } - t.setHour(uint8(v)) - return input[2:], true -} - func secondsNumeric(t *CoreTime, input string, ctx map[string]int) (string, bool) { result := oneOrTwoDigitRegex.FindString(input) length := len(result) From 777907bceef42828455c8329aa1ab203fbcabb6d Mon Sep 17 00:00:00 2001 From: "Zhuomin(Charming) Liu" Date: Thu, 28 May 2020 18:11:13 +0800 Subject: [PATCH 60/74] planner: push agg below partition union all by default (#17262) --- cmd/explaintest/r/generated_columns.result | 4 +- cmd/explaintest/r/partition_pruning.result | 502 +++++++++--------- cmd/explaintest/r/select.result | 4 +- expression/testdata/partition_pruner_out.json | 2 +- planner/core/exhaust_physical_plans.go | 9 + planner/core/initialize.go | 6 + planner/core/logical_plans.go | 5 + planner/core/rule_aggregation_push_down.go | 50 +- planner/core/rule_partition_processor.go | 4 +- planner/core/stringer.go | 10 +- planner/core/testdata/analyze_suite_out.json | 4 +- .../core/testdata/integration_suite_out.json | 25 +- planner/core/testdata/plan_suite_out.json | 36 +- .../testdata/plan_suite_unexported_out.json | 18 +- util/plancodec/id.go | 2 + 15 files changed, 361 insertions(+), 320 deletions(-) diff --git a/cmd/explaintest/r/generated_columns.result b/cmd/explaintest/r/generated_columns.result index c29348e67696a..b7e5a7f68c76c 100644 --- a/cmd/explaintest/r/generated_columns.result +++ b/cmd/explaintest/r/generated_columns.result @@ -104,7 +104,7 @@ PARTITION p5 VALUES LESS THAN (6), PARTITION max VALUES LESS THAN MAXVALUE); EXPLAIN SELECT * FROM sgc3 WHERE a <= 1; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.sgc3.a, 1) │ └─TableFullScan_9 10000.00 cop[tikv] table:sgc3, partition:p0 keep order:false, stats:pseudo @@ -113,7 +113,7 @@ Union_8 6646.67 root └─TableFullScan_12 10000.00 cop[tikv] table:sgc3, partition:p1 keep order:false, stats:pseudo EXPLAIN SELECT * FROM sgc3 WHERE a < 7; id estRows task access object operator info -Union_13 23263.33 root +PartitionUnion_13 23263.33 root ├─TableReader_16 3323.33 root data:Selection_15 │ └─Selection_15 3323.33 cop[tikv] lt(test.sgc3.a, 7) │ └─TableFullScan_14 10000.00 cop[tikv] table:sgc3, partition:p0 keep order:false, stats:pseudo diff --git a/cmd/explaintest/r/partition_pruning.result b/cmd/explaintest/r/partition_pruning.result index a91fa7c3b7405..d5d6d994965f2 100644 --- a/cmd/explaintest/r/partition_pruning.result +++ b/cmd/explaintest/r/partition_pruning.result @@ -16,14 +16,14 @@ PARTITION max VALUES LESS THAN MAXVALUE); INSERT INTO t1 VALUES (-1),(0),(1),(2),(3),(4),(5),(6),(7),(8); EXPLAIN SELECT * FROM t1 WHERE a <= 1; id estRows task access object operator info -Union_8 2.00 root +PartitionUnion_8 2.00 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,1], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p1 range:[-inf,1], keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE a < 7; id estRows task access object operator info -Union_13 49.00 root +PartitionUnion_13 49.00 root ├─TableReader_15 3333.33 root data:TableRangeScan_14 │ └─TableRangeScan_14 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,7), keep order:false, stats:pseudo ├─TableReader_17 3333.33 root data:TableRangeScan_16 @@ -40,7 +40,7 @@ Union_13 49.00 root └─TableRangeScan_26 3333.33 cop[tikv] table:t1, partition:max range:[-inf,7), keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE a <= 1; id estRows task access object operator info -Union_8 2.00 root +PartitionUnion_8 2.00 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,1], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -74,7 +74,7 @@ a 1 EXPLAIN SELECT * FROM t1 WHERE a < 2; id estRows task access object operator info -Union_8 4.00 root +PartitionUnion_8 4.00 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,2), keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -87,7 +87,7 @@ a 2 EXPLAIN SELECT * FROM t1 WHERE a < 3; id estRows task access object operator info -Union_9 9.00 root +PartitionUnion_9 9.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,3), keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -103,7 +103,7 @@ a 3 EXPLAIN SELECT * FROM t1 WHERE a < 4; id estRows task access object operator info -Union_10 16.00 root +PartitionUnion_10 16.00 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,4), keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -122,7 +122,7 @@ a 4 EXPLAIN SELECT * FROM t1 WHERE a < 5; id estRows task access object operator info -Union_11 25.00 root +PartitionUnion_11 25.00 root ├─TableReader_13 3333.33 root data:TableRangeScan_12 │ └─TableRangeScan_12 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,5), keep order:false, stats:pseudo ├─TableReader_15 3333.33 root data:TableRangeScan_14 @@ -144,7 +144,7 @@ a 5 EXPLAIN SELECT * FROM t1 WHERE a < 6; id estRows task access object operator info -Union_12 36.00 root +PartitionUnion_12 36.00 root ├─TableReader_14 3333.33 root data:TableRangeScan_13 │ └─TableRangeScan_13 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,6), keep order:false, stats:pseudo ├─TableReader_16 3333.33 root data:TableRangeScan_15 @@ -169,7 +169,7 @@ a 6 EXPLAIN SELECT * FROM t1 WHERE a < 7; id estRows task access object operator info -Union_13 49.00 root +PartitionUnion_13 49.00 root ├─TableReader_15 3333.33 root data:TableRangeScan_14 │ └─TableRangeScan_14 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,7), keep order:false, stats:pseudo ├─TableReader_17 3333.33 root data:TableRangeScan_16 @@ -191,7 +191,7 @@ a 1 EXPLAIN SELECT * FROM t1 WHERE a <= 1; id estRows task access object operator info -Union_8 2.00 root +PartitionUnion_8 2.00 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,1], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -204,7 +204,7 @@ a 2 EXPLAIN SELECT * FROM t1 WHERE a <= 2; id estRows task access object operator info -Union_9 6.00 root +PartitionUnion_9 6.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,2], keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -220,7 +220,7 @@ a 3 EXPLAIN SELECT * FROM t1 WHERE a <= 3; id estRows task access object operator info -Union_10 12.00 root +PartitionUnion_10 12.00 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,3], keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -239,7 +239,7 @@ a 4 EXPLAIN SELECT * FROM t1 WHERE a <= 4; id estRows task access object operator info -Union_11 20.00 root +PartitionUnion_11 20.00 root ├─TableReader_13 3333.33 root data:TableRangeScan_12 │ └─TableRangeScan_12 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,4], keep order:false, stats:pseudo ├─TableReader_15 3333.33 root data:TableRangeScan_14 @@ -261,7 +261,7 @@ a 5 EXPLAIN SELECT * FROM t1 WHERE a <= 5; id estRows task access object operator info -Union_12 30.00 root +PartitionUnion_12 30.00 root ├─TableReader_14 3333.33 root data:TableRangeScan_13 │ └─TableRangeScan_13 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,5], keep order:false, stats:pseudo ├─TableReader_16 3333.33 root data:TableRangeScan_15 @@ -286,7 +286,7 @@ a 6 EXPLAIN SELECT * FROM t1 WHERE a <= 6; id estRows task access object operator info -Union_13 42.00 root +PartitionUnion_13 42.00 root ├─TableReader_15 3333.33 root data:TableRangeScan_14 │ └─TableRangeScan_14 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,6], keep order:false, stats:pseudo ├─TableReader_17 3333.33 root data:TableRangeScan_16 @@ -314,7 +314,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a <= 7; id estRows task access object operator info -Union_13 49.00 root +PartitionUnion_13 49.00 root ├─TableReader_15 3333.33 root data:TableRangeScan_14 │ └─TableRangeScan_14 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,7], keep order:false, stats:pseudo ├─TableReader_17 3333.33 root data:TableRangeScan_16 @@ -383,7 +383,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a >= 1; id estRows task access object operator info -Union_12 20000.00 root +PartitionUnion_12 20000.00 root ├─TableReader_14 3333.33 root data:TableRangeScan_13 │ └─TableRangeScan_13 3333.33 cop[tikv] table:t1, partition:p1 range:[1,+inf], keep order:false, stats:pseudo ├─TableReader_16 3333.33 root data:TableRangeScan_15 @@ -407,7 +407,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a >= 2; id estRows task access object operator info -Union_11 16666.67 root +PartitionUnion_11 16666.67 root ├─TableReader_13 3333.33 root data:TableRangeScan_12 │ └─TableRangeScan_12 3333.33 cop[tikv] table:t1, partition:p2 range:[2,+inf], keep order:false, stats:pseudo ├─TableReader_15 3333.33 root data:TableRangeScan_14 @@ -428,7 +428,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a >= 3; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p3 range:[3,+inf], keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -446,7 +446,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a >= 4; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p4 range:[4,+inf], keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -461,7 +461,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a >= 5; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p5 range:[5,+inf], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -494,7 +494,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a > 1; id estRows task access object operator info -Union_11 16666.67 root +PartitionUnion_11 16666.67 root ├─TableReader_13 3333.33 root data:TableRangeScan_12 │ └─TableRangeScan_12 3333.33 cop[tikv] table:t1, partition:p2 range:(1,+inf], keep order:false, stats:pseudo ├─TableReader_15 3333.33 root data:TableRangeScan_14 @@ -515,7 +515,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a > 2; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p3 range:(2,+inf], keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -533,7 +533,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a > 3; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p4 range:(3,+inf], keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -548,7 +548,7 @@ a 8 EXPLAIN SELECT * FROM t1 WHERE a > 4; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p5 range:(4,+inf], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -602,7 +602,7 @@ a 1 EXPLAIN SELECT * FROM t1 WHERE a < 2; id estRows task access object operator info -Union_8 4.00 root +PartitionUnion_8 4.00 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,2), keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -615,7 +615,7 @@ a 2 EXPLAIN SELECT * FROM t1 WHERE a < 3; id estRows task access object operator info -Union_9 9.00 root +PartitionUnion_9 9.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,3), keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -631,7 +631,7 @@ a 3 EXPLAIN SELECT * FROM t1 WHERE a < 4; id estRows task access object operator info -Union_10 16.00 root +PartitionUnion_10 16.00 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,4), keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -650,7 +650,7 @@ a 4 EXPLAIN SELECT * FROM t1 WHERE a < 5; id estRows task access object operator info -Union_11 25.00 root +PartitionUnion_11 25.00 root ├─TableReader_13 3333.33 root data:TableRangeScan_12 │ └─TableRangeScan_12 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,5), keep order:false, stats:pseudo ├─TableReader_15 3333.33 root data:TableRangeScan_14 @@ -672,7 +672,7 @@ a 5 EXPLAIN SELECT * FROM t1 WHERE a < 6; id estRows task access object operator info -Union_12 36.00 root +PartitionUnion_12 36.00 root ├─TableReader_14 3333.33 root data:TableRangeScan_13 │ └─TableRangeScan_13 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,6), keep order:false, stats:pseudo ├─TableReader_16 3333.33 root data:TableRangeScan_15 @@ -692,7 +692,7 @@ a 1 EXPLAIN SELECT * FROM t1 WHERE a <= 1; id estRows task access object operator info -Union_8 2.00 root +PartitionUnion_8 2.00 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,1], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -705,7 +705,7 @@ a 2 EXPLAIN SELECT * FROM t1 WHERE a <= 2; id estRows task access object operator info -Union_9 6.00 root +PartitionUnion_9 6.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,2], keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -721,7 +721,7 @@ a 3 EXPLAIN SELECT * FROM t1 WHERE a <= 3; id estRows task access object operator info -Union_10 12.00 root +PartitionUnion_10 12.00 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,3], keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -740,7 +740,7 @@ a 4 EXPLAIN SELECT * FROM t1 WHERE a <= 4; id estRows task access object operator info -Union_11 20.00 root +PartitionUnion_11 20.00 root ├─TableReader_13 3333.33 root data:TableRangeScan_12 │ └─TableRangeScan_12 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,4], keep order:false, stats:pseudo ├─TableReader_15 3333.33 root data:TableRangeScan_14 @@ -762,7 +762,7 @@ a 5 EXPLAIN SELECT * FROM t1 WHERE a <= 5; id estRows task access object operator info -Union_12 30.00 root +PartitionUnion_12 30.00 root ├─TableReader_14 3333.33 root data:TableRangeScan_13 │ └─TableRangeScan_13 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,5], keep order:false, stats:pseudo ├─TableReader_16 3333.33 root data:TableRangeScan_15 @@ -787,7 +787,7 @@ a 6 EXPLAIN SELECT * FROM t1 WHERE a <= 6; id estRows task access object operator info -Union_12 36.00 root +PartitionUnion_12 36.00 root ├─TableReader_14 3333.33 root data:TableRangeScan_13 │ └─TableRangeScan_13 3333.33 cop[tikv] table:t1, partition:p0 range:[-inf,6], keep order:false, stats:pseudo ├─TableReader_16 3333.33 root data:TableRangeScan_15 @@ -847,7 +847,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a >= 1; id estRows task access object operator info -Union_11 16666.67 root +PartitionUnion_11 16666.67 root ├─TableReader_13 3333.33 root data:TableRangeScan_12 │ └─TableRangeScan_12 3333.33 cop[tikv] table:t1, partition:p1 range:[1,+inf], keep order:false, stats:pseudo ├─TableReader_15 3333.33 root data:TableRangeScan_14 @@ -868,7 +868,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a >= 2; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p2 range:[2,+inf], keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -886,7 +886,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a >= 3; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p3 range:[3,+inf], keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -901,7 +901,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a >= 4; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p4 range:[4,+inf], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -933,7 +933,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a > 1; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_12 3333.33 root data:TableRangeScan_11 │ └─TableRangeScan_11 3333.33 cop[tikv] table:t1, partition:p2 range:(1,+inf], keep order:false, stats:pseudo ├─TableReader_14 3333.33 root data:TableRangeScan_13 @@ -951,7 +951,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a > 2; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_11 3333.33 root data:TableRangeScan_10 │ └─TableRangeScan_10 3333.33 cop[tikv] table:t1, partition:p3 range:(2,+inf], keep order:false, stats:pseudo ├─TableReader_13 3333.33 root data:TableRangeScan_12 @@ -966,7 +966,7 @@ a 7 EXPLAIN SELECT * FROM t1 WHERE a > 3; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_10 3333.33 root data:TableRangeScan_9 │ └─TableRangeScan_9 3333.33 cop[tikv] table:t1, partition:p4 range:(3,+inf], keep order:false, stats:pseudo └─TableReader_12 3333.33 root data:TableRangeScan_11 @@ -1026,7 +1026,7 @@ INSERT INTO t1 VALUES (1, '2009-01-01'), (1, '2009-04-01'), (2, '2009-04-01'), (1, '2009-04-05'), (1, '2009-04-06'), (1, '2009-04-07'); EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1038,7 +1038,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1055,7 +1055,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1067,7 +1067,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1080,7 +1080,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1090,7 +1090,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1106,7 +1106,7 @@ IndexReader_11 10.00 root index:Selection_10 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1122,7 +1122,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1137,7 +1137,7 @@ Union_10 13333.33 root └─IndexFullScan_32 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1149,7 +1149,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1166,7 +1166,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1178,7 +1178,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1190,7 +1190,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1202,7 +1202,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1219,7 +1219,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1231,7 +1231,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1243,7 +1243,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1252,7 +1252,7 @@ Union_8 6646.67 root └─IndexFullScan_18 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1266,7 +1266,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1281,7 +1281,7 @@ Union_10 13333.33 root └─IndexFullScan_32 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1296,7 +1296,7 @@ Union_10 13333.33 root └─IndexFullScan_32 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1308,7 +1308,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1325,7 +1325,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1337,7 +1337,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1350,7 +1350,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1363,7 +1363,7 @@ Union_9 9970.00 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1382,7 +1382,7 @@ IndexReader_11 10.00 root index:Selection_10 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1395,7 +1395,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1408,7 +1408,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1418,7 +1418,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1434,7 +1434,7 @@ IndexReader_11 10.00 root index:Selection_10 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1450,7 +1450,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1481,7 +1481,7 @@ INSERT INTO t1 VALUES (1, '2009-01-01'), (1, '2009-04-01'), (2, '2009-04-01'), (1, '2009-04-07'); EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1493,7 +1493,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1510,7 +1510,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1522,7 +1522,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1535,7 +1535,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1545,7 +1545,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1561,7 +1561,7 @@ IndexReader_11 0.00 root index:Selection_10 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1577,7 +1577,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1592,7 +1592,7 @@ Union_10 13333.33 root └─IndexFullScan_32 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1604,7 +1604,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1621,7 +1621,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1633,7 +1633,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1645,7 +1645,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1657,7 +1657,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1674,7 +1674,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1686,7 +1686,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1698,7 +1698,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1707,7 +1707,7 @@ Union_8 6646.67 root └─IndexFullScan_18 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1721,7 +1721,7 @@ IndexReader_11 0.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1736,7 +1736,7 @@ Union_10 13333.33 root └─IndexFullScan_32 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1751,7 +1751,7 @@ Union_10 13333.33 root └─IndexFullScan_32 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1763,7 +1763,7 @@ Union_9 9970.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1780,7 +1780,7 @@ IndexReader_11 10.00 root index:Selection_10 └─IndexFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1792,7 +1792,7 @@ Union_9 10000.00 root └─IndexFullScan_25 10000.00 cop[tikv] table:t1, partition:p20090405, index:PRIMARY(a, b) keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1805,7 +1805,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1818,7 +1818,7 @@ Union_9 9970.00 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─IndexReader_15 3323.33 root index:Selection_14 │ └─Selection_14 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1837,7 +1837,7 @@ IndexReader_11 0.00 root index:Selection_10 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1850,7 +1850,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─IndexReader_15 3333.33 root index:Selection_14 │ └─Selection_14 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:01) │ └─IndexFullScan_13 10000.00 cop[tikv] table:t1, partition:p20090403, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1863,7 +1863,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1873,7 +1873,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─IndexReader_14 3323.33 root index:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1889,7 +1889,7 @@ IndexReader_11 0.00 root index:Selection_10 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1905,7 +1905,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─IndexReader_16 3333.33 root index:Selection_15 │ └─Selection_15 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:58) │ └─IndexFullScan_14 10000.00 cop[tikv] table:t1, partition:p20090402, index:PRIMARY(a, b) keep order:false, stats:pseudo @@ -1935,7 +1935,7 @@ INSERT INTO t1 VALUES (1, '2009-01-01'), (1, '2009-04-01'), (2, '2009-04-01'), (1, '2009-04-05'), (1, '2009-04-06'), (1, '2009-04-07'); EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -1947,7 +1947,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -1964,7 +1964,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -1976,7 +1976,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -1989,7 +1989,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -1999,7 +1999,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2015,7 +2015,7 @@ TableReader_8 10.00 root data:Selection_7 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2031,7 +2031,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2046,7 +2046,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2058,7 +2058,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2075,7 +2075,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2087,7 +2087,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2099,7 +2099,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2111,7 +2111,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2128,7 +2128,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2140,7 +2140,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2152,7 +2152,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2161,7 +2161,7 @@ Union_8 6646.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2175,7 +2175,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2190,7 +2190,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2205,7 +2205,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2217,7 +2217,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2234,7 +2234,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2246,7 +2246,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2259,7 +2259,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2272,7 +2272,7 @@ Union_9 9970.00 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2291,7 +2291,7 @@ TableReader_8 10.00 root data:Selection_7 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2304,7 +2304,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2317,7 +2317,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2327,7 +2327,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2343,7 +2343,7 @@ TableReader_8 10.00 root data:Selection_7 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2359,7 +2359,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2389,7 +2389,7 @@ INSERT INTO t1 VALUES (1, '2009-01-01'), (1, '2009-04-01'), (2, '2009-04-01'), (1, '2009-04-07'); EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2401,7 +2401,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2418,7 +2418,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2430,7 +2430,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2443,7 +2443,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2453,7 +2453,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2469,7 +2469,7 @@ TableReader_8 0.00 root data:Selection_7 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2485,7 +2485,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:59' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2500,7 +2500,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2512,7 +2512,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2529,7 +2529,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2541,7 +2541,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03' AS DATE); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2553,7 +2553,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2565,7 +2565,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2582,7 +2582,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2594,7 +2594,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03 00:00:00'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2606,7 +2606,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2615,7 +2615,7 @@ Union_8 6646.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2629,7 +2629,7 @@ TableReader_8 0.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2644,7 +2644,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-02 23:59:59'; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:59.000000) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2659,7 +2659,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b < '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2671,7 +2671,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b <= '2009-04-03'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2688,7 +2688,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b >= '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2700,7 +2700,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p20090405 keep order:false, stats:pseudo EXPLAIN SELECT * FROM t1 WHERE b > '2009-04-03'; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2713,7 +2713,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2726,7 +2726,7 @@ Union_9 9970.00 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2745,7 +2745,7 @@ TableReader_8 0.00 root data:Selection_7 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2758,7 +2758,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-03 00:00:01' AS DATETIME); id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] gt(test.t1.b, 2009-04-03 00:00:01) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p20090403 keep order:false, stats:pseudo @@ -2771,7 +2771,7 @@ Union_9 10000.00 root EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2781,7 +2781,7 @@ Union_8 6646.67 root EXPLAIN SELECT * FROM t1 WHERE b <= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2797,7 +2797,7 @@ TableReader_8 0.00 root data:Selection_7 EXPLAIN SELECT * FROM t1 WHERE b >= CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2813,7 +2813,7 @@ Union_10 13333.33 root EXPLAIN SELECT * FROM t1 WHERE b > CAST('2009-04-02 23:59:58' AS DATETIME); id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t1.b, 2009-04-02 23:59:58) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p20090402 keep order:false, stats:pseudo @@ -2841,7 +2841,7 @@ INSERT INTO t1 VALUES (1, '2009-01-01'), (2, NULL); # test with an invalid date, which lead to item->null_value is set. EXPLAIN SELECT * FROM t1 WHERE b < CAST('2009-04-99' AS DATETIME); id estRows task access object operator info -Union_11 0.00 root +PartitionUnion_11 0.00 root ├─TableReader_14 0.00 root data:Selection_13 │ └─Selection_13 0.00 cop[tikv] lt(test.t1.b, NULL) │ └─TableFullScan_12 10000.00 cop[tikv] table:t1, partition:p20090401 keep order:false, stats:pseudo @@ -2880,7 +2880,7 @@ partition p1 values less than (20) insert into t3 values (5),(15); explain select * from t3 where a=11; id estRows task access object operator info -Union_8 20.00 root +PartitionUnion_8 20.00 root ├─TableReader_11 10.00 root data:Selection_10 │ └─Selection_10 10.00 cop[tikv] eq(test.t3.a, 11) │ └─TableFullScan_9 10000.00 cop[tikv] table:t3, partition:p0 keep order:false, stats:pseudo @@ -2889,7 +2889,7 @@ Union_8 20.00 root └─TableFullScan_12 10000.00 cop[tikv] table:t3, partition:p1 keep order:false, stats:pseudo explain select * from t3 where a=10; id estRows task access object operator info -Union_8 20.00 root +PartitionUnion_8 20.00 root ├─TableReader_11 10.00 root data:Selection_10 │ └─Selection_10 10.00 cop[tikv] eq(test.t3.a, 10) │ └─TableFullScan_9 10000.00 cop[tikv] table:t3, partition:p0 keep order:false, stats:pseudo @@ -2898,7 +2898,7 @@ Union_8 20.00 root └─TableFullScan_12 10000.00 cop[tikv] table:t3, partition:p1 keep order:false, stats:pseudo explain select * from t3 where a=20; id estRows task access object operator info -Union_8 20.00 root +PartitionUnion_8 20.00 root ├─TableReader_11 10.00 root data:Selection_10 │ └─Selection_10 10.00 cop[tikv] eq(test.t3.a, 20) │ └─TableFullScan_9 10000.00 cop[tikv] table:t3, partition:p0 keep order:false, stats:pseudo @@ -2907,7 +2907,7 @@ Union_8 20.00 root └─TableFullScan_12 10000.00 cop[tikv] table:t3, partition:p1 keep order:false, stats:pseudo explain select * from t3 where a=30; id estRows task access object operator info -Union_8 20.00 root +PartitionUnion_8 20.00 root ├─TableReader_11 10.00 root data:Selection_10 │ └─Selection_10 10.00 cop[tikv] eq(test.t3.a, 30) │ └─TableFullScan_9 10000.00 cop[tikv] table:t3, partition:p0 keep order:false, stats:pseudo @@ -2944,7 +2944,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo explain select * from t7 where a >= 9; id estRows task access object operator info -Union_11 16666.67 root +PartitionUnion_11 16666.67 root ├─TableReader_14 3333.33 root data:Selection_13 │ └─Selection_13 3333.33 cop[tikv] ge(test.t7.a, 9) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -2962,7 +2962,7 @@ Union_11 16666.67 root └─TableFullScan_24 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a > 9; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t7.a, 9) │ └─TableFullScan_11 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo @@ -2982,7 +2982,7 @@ TableReader_8 3323.33 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo explain select * from t7 where a <= 10; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t7.a, 10) │ └─TableFullScan_9 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -2996,7 +2996,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo explain select * from t7 where a >= 10; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t7.a, 10) │ └─TableFullScan_11 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo @@ -3011,7 +3011,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a > 10; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t7.a, 10) │ └─TableFullScan_11 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo @@ -3026,7 +3026,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a < 89; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t7.a, 89) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3044,7 +3044,7 @@ Union_11 16616.67 root └─TableFullScan_24 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a <= 89; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t7.a, 89) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3075,7 +3075,7 @@ TableReader_8 3333.33 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a < 90; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t7.a, 90) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3093,7 +3093,7 @@ Union_11 16616.67 root └─TableFullScan_24 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a <= 90; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t7.a, 90) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3157,7 +3157,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo explain select * from t7 where a >= 9; id estRows task access object operator info -Union_11 16666.67 root +PartitionUnion_11 16666.67 root ├─TableReader_14 3333.33 root data:Selection_13 │ └─Selection_13 3333.33 cop[tikv] ge(test.t7.a, 9) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3175,7 +3175,7 @@ Union_11 16666.67 root └─TableFullScan_24 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a > 9; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t7.a, 9) │ └─TableFullScan_11 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo @@ -3195,7 +3195,7 @@ TableReader_8 3323.33 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo explain select * from t7 where a <= 10; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t7.a, 10) │ └─TableFullScan_9 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3209,7 +3209,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo explain select * from t7 where a >= 10; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] ge(test.t7.a, 10) │ └─TableFullScan_11 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo @@ -3224,7 +3224,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a > 10; id estRows task access object operator info -Union_10 13333.33 root +PartitionUnion_10 13333.33 root ├─TableReader_13 3333.33 root data:Selection_12 │ └─Selection_12 3333.33 cop[tikv] gt(test.t7.a, 10) │ └─TableFullScan_11 10000.00 cop[tikv] table:t7, partition:p30 keep order:false, stats:pseudo @@ -3239,7 +3239,7 @@ Union_10 13333.33 root └─TableFullScan_20 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a < 89; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t7.a, 89) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3257,7 +3257,7 @@ Union_11 16616.67 root └─TableFullScan_24 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a <= 89; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t7.a, 89) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3288,7 +3288,7 @@ TableReader_8 3333.33 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a < 90; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] lt(test.t7.a, 90) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3306,7 +3306,7 @@ Union_11 16616.67 root └─TableFullScan_24 10000.00 cop[tikv] table:t7, partition:p90 keep order:false, stats:pseudo explain select * from t7 where a <= 90; id estRows task access object operator info -Union_11 16616.67 root +PartitionUnion_11 16616.67 root ├─TableReader_14 3323.33 root data:Selection_13 │ └─Selection_13 3323.33 cop[tikv] le(test.t7.a, 90) │ └─TableFullScan_12 10000.00 cop[tikv] table:t7, partition:p10 keep order:false, stats:pseudo @@ -3347,7 +3347,7 @@ partition p2 values less than (2000) insert into t8 values ('1985-05-05'),('1995-05-05'); explain select * from t8 where a < '1980-02-02'; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t8.a, 1980-02-02 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t8, partition:p0 keep order:false, stats:pseudo @@ -3365,7 +3365,7 @@ partition p2 values less than (732664) -- 2005-12-19 insert into t9 values ('2005-05-05'), ('2005-04-04'); explain select * from t9 where a < '2004-12-19'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t9.a, 2004-12-19 00:00:00.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t9, partition:p0 keep order:false, stats:pseudo @@ -3374,7 +3374,7 @@ Union_8 6646.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t9, partition:p1 keep order:false, stats:pseudo explain select * from t9 where a <= '2004-12-19'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t9.a, 2004-12-19 00:00:00.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t9, partition:p0 keep order:false, stats:pseudo @@ -3393,7 +3393,7 @@ partition p2 values less than (9) insert into t1 values (1),(2),(3); explain select * from t1 where a1 > 3; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_11 3333.33 root data:Selection_10 │ └─Selection_10 3333.33 cop[tikv] gt(test.t1.a1, 3) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p1 keep order:false, stats:pseudo @@ -3402,7 +3402,7 @@ Union_8 6666.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t1, partition:p2 keep order:false, stats:pseudo explain select * from t1 where a1 >= 3; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_11 3333.33 root data:Selection_10 │ └─Selection_10 3333.33 cop[tikv] ge(test.t1.a1, 3) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p1 keep order:false, stats:pseudo @@ -3441,7 +3441,7 @@ insert into t2 select a,4 from t1 where a >= 600 and a < 800; insert into t2 select a,5 from t1 where a >= 800 and a < 1001; explain select * from t2; id estRows task access object operator info -Union_10 50000.00 root +PartitionUnion_10 50000.00 root ├─TableReader_12 10000.00 root data:TableFullScan_11 │ └─TableFullScan_11 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo ├─TableReader_14 10000.00 root data:TableFullScan_13 @@ -3454,7 +3454,7 @@ Union_10 50000.00 root └─TableFullScan_19 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where a < 801 and a > 200; id estRows task access object operator info -Union_10 1000.00 root +PartitionUnion_10 1000.00 root ├─TableReader_13 250.00 root data:Selection_12 │ └─Selection_12 250.00 cop[tikv] gt(test.t2.a, 200), lt(test.t2.a, 801) │ └─TableFullScan_11 10000.00 cop[tikv] table:t2, partition:p1 keep order:false, stats:pseudo @@ -3474,7 +3474,7 @@ TableReader_8 0.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where a > 600; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_11 3333.33 root data:Selection_10 │ └─Selection_10 3333.33 cop[tikv] gt(test.t2.a, 600) │ └─TableFullScan_9 10000.00 cop[tikv] table:t2, partition:p3 keep order:false, stats:pseudo @@ -3483,7 +3483,7 @@ Union_8 6666.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where a > 600 and b = 1; id estRows task access object operator info -Union_8 6.67 root +PartitionUnion_8 6.67 root ├─TableReader_11 3.33 root data:Selection_10 │ └─Selection_10 3.33 cop[tikv] eq(test.t2.b, 1), gt(test.t2.a, 600) │ └─TableFullScan_9 10000.00 cop[tikv] table:t2, partition:p3 keep order:false, stats:pseudo @@ -3492,7 +3492,7 @@ Union_8 6.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where a > 600 and b = 4; id estRows task access object operator info -Union_8 6.67 root +PartitionUnion_8 6.67 root ├─TableReader_11 3.33 root data:Selection_10 │ └─Selection_10 3.33 cop[tikv] eq(test.t2.b, 4), gt(test.t2.a, 600) │ └─TableFullScan_9 10000.00 cop[tikv] table:t2, partition:p3 keep order:false, stats:pseudo @@ -3501,7 +3501,7 @@ Union_8 6.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where a > 600 and b = 5; id estRows task access object operator info -Union_8 6.67 root +PartitionUnion_8 6.67 root ├─TableReader_11 3.33 root data:Selection_10 │ └─Selection_10 3.33 cop[tikv] eq(test.t2.b, 5), gt(test.t2.a, 600) │ └─TableFullScan_9 10000.00 cop[tikv] table:t2, partition:p3 keep order:false, stats:pseudo @@ -3510,7 +3510,7 @@ Union_8 6.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b = 5; id estRows task access object operator info -Union_11 50.00 root +PartitionUnion_11 50.00 root ├─TableReader_14 10.00 root data:Selection_13 │ └─Selection_13 10.00 cop[tikv] eq(test.t2.b, 5) │ └─TableFullScan_12 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3569,7 +3569,7 @@ insert into t2 select a,8 from t1 where a >= 800 and a < 900; insert into t2 select a,9 from t1 where a >= 900 and a < 1001; explain select * from t2; id estRows task access object operator info -Union_10 50000.00 root +PartitionUnion_10 50000.00 root ├─TableReader_12 10000.00 root data:TableFullScan_11 │ └─TableFullScan_11 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo ├─TableReader_14 10000.00 root data:TableFullScan_13 @@ -3602,7 +3602,7 @@ TableReader_8 30.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo explain select * from t2 where (a = 100 OR a = 900); id estRows task access object operator info -Union_8 40.00 root +PartitionUnion_8 40.00 root ├─TableReader_11 20.00 root data:Selection_10 │ └─Selection_10 20.00 cop[tikv] or(eq(test.t2.a, 100), eq(test.t2.a, 900)) │ └─TableFullScan_9 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3611,7 +3611,7 @@ Union_8 40.00 root └─TableFullScan_12 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where (a > 100 AND a < 600); id estRows task access object operator info -Union_9 750.00 root +PartitionUnion_9 750.00 root ├─TableReader_12 250.00 root data:Selection_11 │ └─Selection_11 250.00 cop[tikv] gt(test.t2.a, 100), lt(test.t2.a, 600) │ └─TableFullScan_10 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3623,7 +3623,7 @@ Union_9 750.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t2, partition:p2 keep order:false, stats:pseudo explain select * from t2 where b = 4; id estRows task access object operator info -Union_11 50.00 root +PartitionUnion_11 50.00 root ├─IndexLookUp_17 10.00 root │ ├─IndexRangeScan_15(Build) 10.00 cop[tikv] table:t2, partition:p0, index:b(b) range:[4,4], keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 10.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3641,7 +3641,7 @@ Union_11 50.00 root └─TableRowIDScan_40(Probe) 10.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b = 6; id estRows task access object operator info -Union_11 50.00 root +PartitionUnion_11 50.00 root ├─IndexLookUp_17 10.00 root │ ├─IndexRangeScan_15(Build) 10.00 cop[tikv] table:t2, partition:p0, index:b(b) range:[6,6], keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 10.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3659,7 +3659,7 @@ Union_11 50.00 root └─TableRowIDScan_40(Probe) 10.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b in (1,3,5); id estRows task access object operator info -Union_11 150.00 root +PartitionUnion_11 150.00 root ├─IndexLookUp_17 30.00 root │ ├─IndexRangeScan_15(Build) 30.00 cop[tikv] table:t2, partition:p0, index:b(b) range:[1,1], [3,3], [5,5], keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 30.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3677,7 +3677,7 @@ Union_11 150.00 root └─TableRowIDScan_40(Probe) 30.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b in (2,4,6); id estRows task access object operator info -Union_11 150.00 root +PartitionUnion_11 150.00 root ├─IndexLookUp_17 30.00 root │ ├─IndexRangeScan_15(Build) 30.00 cop[tikv] table:t2, partition:p0, index:b(b) range:[2,2], [4,4], [6,6], keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 30.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3695,7 +3695,7 @@ Union_11 150.00 root └─TableRowIDScan_40(Probe) 30.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b in (7,8,9); id estRows task access object operator info -Union_11 150.00 root +PartitionUnion_11 150.00 root ├─IndexLookUp_17 30.00 root │ ├─IndexRangeScan_15(Build) 30.00 cop[tikv] table:t2, partition:p0, index:b(b) range:[7,7], [8,8], [9,9], keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 30.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3713,7 +3713,7 @@ Union_11 150.00 root └─TableRowIDScan_40(Probe) 30.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b > 5; id estRows task access object operator info -Union_11 16666.67 root +PartitionUnion_11 16666.67 root ├─TableReader_14 3333.33 root data:Selection_13 │ └─Selection_13 3333.33 cop[tikv] gt(test.t2.b, 5) │ └─TableFullScan_12 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3731,7 +3731,7 @@ Union_11 16666.67 root └─TableFullScan_36 10000.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b > 5 and b < 8; id estRows task access object operator info -Union_11 1250.00 root +PartitionUnion_11 1250.00 root ├─IndexLookUp_17 250.00 root │ ├─IndexRangeScan_15(Build) 250.00 cop[tikv] table:t2, partition:p0, index:b(b) range:(5,8), keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 250.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3749,7 +3749,7 @@ Union_11 1250.00 root └─TableRowIDScan_40(Probe) 250.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b > 5 and b < 7; id estRows task access object operator info -Union_11 1250.00 root +PartitionUnion_11 1250.00 root ├─IndexLookUp_17 250.00 root │ ├─IndexRangeScan_15(Build) 250.00 cop[tikv] table:t2, partition:p0, index:b(b) range:(5,7), keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 250.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3767,7 +3767,7 @@ Union_11 1250.00 root └─TableRowIDScan_40(Probe) 250.00 cop[tikv] table:t2, partition:p4 keep order:false, stats:pseudo explain select * from t2 where b > 0 and b < 5; id estRows task access object operator info -Union_11 1250.00 root +PartitionUnion_11 1250.00 root ├─IndexLookUp_17 250.00 root │ ├─IndexRangeScan_15(Build) 250.00 cop[tikv] table:t2, partition:p0, index:b(b) range:(0,5), keep order:false, stats:pseudo │ └─TableRowIDScan_16(Probe) 250.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3859,7 +3859,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo explain select * from t2 where a=0; id estRows task access object operator info -Union_9 30.00 root +PartitionUnion_9 30.00 root ├─TableReader_12 10.00 root data:Selection_11 │ └─Selection_11 10.00 cop[tikv] eq(test.t2.a, 0) │ └─TableFullScan_10 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3876,7 +3876,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p2 keep order:false, stats:pseudo explain select * from t2 where a=0xFE; id estRows task access object operator info -Union_9 30.00 root +PartitionUnion_9 30.00 root ├─TableReader_12 10.00 root data:Selection_11 │ └─Selection_11 10.00 cop[tikv] eq(test.t2.a, 254) │ └─TableFullScan_10 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3891,7 +3891,7 @@ id estRows task access object operator info TableDual_6 0.00 root rows:0 explain select * from t2 where a > 0xFE AND a <= 0xFF; id estRows task access object operator info -Union_9 750.00 root +PartitionUnion_9 750.00 root ├─TableReader_12 250.00 root data:Selection_11 │ └─Selection_11 250.00 cop[tikv] gt(test.t2.a, 254), le(test.t2.a, 255) │ └─TableFullScan_10 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3908,7 +3908,7 @@ TableReader_8 250.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p2 keep order:false, stats:pseudo explain select * from t2 where a >= 0xFE AND a <= 0xFF; id estRows task access object operator info -Union_9 750.00 root +PartitionUnion_9 750.00 root ├─TableReader_12 250.00 root data:Selection_11 │ └─Selection_11 250.00 cop[tikv] ge(test.t2.a, 254), le(test.t2.a, 255) │ └─TableFullScan_10 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3925,7 +3925,7 @@ TableReader_8 250.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo explain select * from t2 where a < 64 AND a >= 63; id estRows task access object operator info -Union_9 750.00 root +PartitionUnion_9 750.00 root ├─TableReader_12 250.00 root data:Selection_11 │ └─Selection_11 250.00 cop[tikv] ge(test.t2.a, 63), lt(test.t2.a, 64) │ └─TableFullScan_10 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3937,7 +3937,7 @@ Union_9 750.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t2, partition:p2 keep order:false, stats:pseudo explain select * from t1 where a <= 64 AND a >= 63; id estRows task access object operator info -Union_8 500.00 root +PartitionUnion_8 500.00 root ├─TableReader_11 250.00 root data:Selection_10 │ └─Selection_10 250.00 cop[tikv] ge(test.t1.a, 63), le(test.t1.a, 64) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo @@ -3946,7 +3946,7 @@ Union_8 500.00 root └─TableFullScan_12 10000.00 cop[tikv] table:t1, partition:p1 keep order:false, stats:pseudo explain select * from t2 where a <= 64 AND a >= 63; id estRows task access object operator info -Union_9 750.00 root +PartitionUnion_9 750.00 root ├─TableReader_12 250.00 root data:Selection_11 │ └─Selection_11 250.00 cop[tikv] ge(test.t2.a, 63), le(test.t2.a, 64) │ └─TableFullScan_10 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo @@ -3969,7 +3969,7 @@ insert into t1 values (9),(19),(0xFFFF0000FFFF000-1), (0xFFFF0000FFFFFFF-1); explain select * from t1 where a >= 2305561538531885056-10 and a <= 2305561538531885056-8; id estRows task access object operator info -Union_10 1000.00 root +PartitionUnion_10 1000.00 root ├─TableReader_13 250.00 root data:Selection_12 │ └─Selection_12 250.00 cop[tikv] ge(test.t1.a, 2305561538531885046), le(test.t1.a, 2305561538531885048) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p1 keep order:false, stats:pseudo @@ -3985,7 +3985,7 @@ Union_10 1000.00 root explain select * from t1 where a > 0xFFFFFFFFFFFFFFEC and a < 0xFFFFFFFFFFFFFFEE; id estRows task access object operator info -Union_10 1000.00 root +PartitionUnion_10 1000.00 root ├─TableReader_13 250.00 root data:Selection_12 │ └─Selection_12 250.00 cop[tikv] gt(test.t1.a, 18446744073709551596), lt(test.t1.a, 18446744073709551598) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p1 keep order:false, stats:pseudo @@ -4000,7 +4000,7 @@ Union_10 1000.00 root └─TableFullScan_20 10000.00 cop[tikv] table:t1, partition:p4 keep order:false, stats:pseudo explain select * from t1 where a>=0 and a <= 0xFFFFFFFFFFFFFFFF; id estRows task access object operator info -Union_10 1000.00 root +PartitionUnion_10 1000.00 root ├─TableReader_13 250.00 root data:Selection_12 │ └─Selection_12 250.00 cop[tikv] ge(test.t1.a, 0), le(test.t1.a, 18446744073709551615) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p1 keep order:false, stats:pseudo @@ -4023,7 +4023,7 @@ partition p4 values less than (1000) insert into t1 values (-15),(-5),(5),(15),(-15),(-5),(5),(15); explain select * from t1 where a>-2 and a <=0; id estRows task access object operator info -Union_10 1000.00 root +PartitionUnion_10 1000.00 root ├─TableReader_13 250.00 root data:Selection_12 │ └─Selection_12 250.00 cop[tikv] gt(test.t1.a, -2), le(test.t1.a, 0) │ └─TableFullScan_11 10000.00 cop[tikv] table:t1, partition:p1 keep order:false, stats:pseudo @@ -4048,7 +4048,7 @@ INSERT INTO t1 VALUES ('2007-03-08 12:00:00'); INSERT INTO t1 VALUES ('2007-03-15 12:00:00'); explain select * from t1 where recdate < '2007-03-08 00:00:00'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.recdate, 2007-03-08 00:00:00.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo @@ -4067,7 +4067,7 @@ INSERT INTO t1 VALUES ('2006-03-01 12:00:00'); INSERT INTO t1 VALUES ('2006-03-01 12:00:00'); explain select * from t1 where recdate < '2006-01-01 00:00:00'; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t1.recdate, 2006-01-01 00:00:00.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo @@ -4086,7 +4086,7 @@ partition p2 values less than (255) insert into t1 select A.a + 10*B.a from t0 A, t0 B; explain select * from t1 where a between 10 and 13; id estRows task access object operator info -Union_9 750.00 root +PartitionUnion_9 750.00 root ├─TableReader_12 250.00 root data:Selection_11 │ └─Selection_11 250.00 cop[tikv] ge(test.t1.a, 10), le(test.t1.a, 13) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo @@ -4098,7 +4098,7 @@ Union_9 750.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t1, partition:p2 keep order:false, stats:pseudo explain select * from t1 where a between 10 and 10+33; id estRows task access object operator info -Union_9 750.00 root +PartitionUnion_9 750.00 root ├─TableReader_12 250.00 root data:Selection_11 │ └─Selection_11 250.00 cop[tikv] ge(test.t1.a, 10), le(test.t1.a, 43) │ └─TableFullScan_10 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo @@ -4113,7 +4113,7 @@ drop table if exists t; create table t(a timestamp) partition by range(unix_timestamp(a)) (partition p0 values less than(unix_timestamp('2019-02-16 14:20:00')), partition p1 values less than (maxvalue)); explain select * from t where a between timestamp'2019-02-16 14:19:00' and timestamp'2019-02-16 14:21:00'; id estRows task access object operator info -Union_8 500.00 root +PartitionUnion_8 500.00 root ├─TableReader_11 250.00 root data:Selection_10 │ └─Selection_10 250.00 cop[tikv] ge(test.t.a, 2019-02-16 14:19:00), le(test.t.a, 2019-02-16 14:21:00) │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4125,7 +4125,7 @@ create table t(a int) partition by range(a) (partition p0 values less than (100) begin; explain select * from t; id estRows task access object operator info -Union_8 30000.00 root +PartitionUnion_8 30000.00 root ├─TableReader_10 10000.00 root data:TableFullScan_9 │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo ├─TableReader_12 10000.00 root data:TableFullScan_11 @@ -4136,7 +4136,7 @@ insert into t values(1); explain select * from t; id estRows task access object operator info Projection_11 30000.00 root test.t.a -└─Union_12 30000.00 root +└─PartitionUnion_12 30000.00 root ├─UnionScan_13 10000.00 root │ └─TableReader_15 10000.00 root data:TableFullScan_14 │ └─TableFullScan_14 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4152,7 +4152,7 @@ insert into t values(101); explain select * from t; id estRows task access object operator info Projection_11 30000.00 root test.t.a -└─Union_12 30000.00 root +└─PartitionUnion_12 30000.00 root ├─UnionScan_13 10000.00 root │ └─TableReader_15 10000.00 root data:TableFullScan_14 │ └─TableFullScan_14 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4168,7 +4168,7 @@ insert into t values(201); explain select * from t; id estRows task access object operator info Projection_11 30000.00 root test.t.a -└─Union_12 30000.00 root +└─PartitionUnion_12 30000.00 root ├─UnionScan_13 10000.00 root │ └─TableReader_15 10000.00 root data:TableFullScan_14 │ └─TableFullScan_14 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4181,7 +4181,7 @@ Projection_11 30000.00 root test.t.a rollback; explain select * from t; id estRows task access object operator info -Union_8 30000.00 root +PartitionUnion_8 30000.00 root ├─TableReader_10 10000.00 root data:TableFullScan_9 │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo ├─TableReader_12 10000.00 root data:TableFullScan_11 @@ -4227,7 +4227,7 @@ TableReader_8 3333.33 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo explain select * from t where ts > '2020-04-14 23:59:59.999' -- p1,p2; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_11 3333.33 root data:Selection_10 │ └─Selection_10 3333.33 cop[tikv] gt(test.t.ts, 2020-04-14 23:59:59.999000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p1 keep order:false, stats:pseudo @@ -4244,7 +4244,7 @@ id estRows task access object operator info TableDual_6 0.00 root rows:0 explain select * from t where ts >= '2020-04-04 12:22:32' -- p0,p1,p2; id estRows task access object operator info -Union_9 10000.00 root +PartitionUnion_9 10000.00 root ├─TableReader_12 3333.33 root data:Selection_11 │ └─Selection_11 3333.33 cop[tikv] ge(test.t.ts, 2020-04-04 12:22:32.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4256,7 +4256,7 @@ Union_9 10000.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo explain select * from t where ts >= '2020-04-05 00:00:00' -- p1,p2; id estRows task access object operator info -Union_8 6666.67 root +PartitionUnion_8 6666.67 root ├─TableReader_11 3333.33 root data:Selection_10 │ └─Selection_10 3333.33 cop[tikv] ge(test.t.ts, 2020-04-05 00:00:00.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p1 keep order:false, stats:pseudo @@ -4268,7 +4268,7 @@ id estRows task access object operator info TableDual_6 0.00 root rows:0 explain select * from t where ts < '2020-04-25 00:00:00' -- p0,p1,p2; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t.ts, 2020-04-25 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4280,7 +4280,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo explain select * from t where ts < '2020-04-15 00:00:00.001' -- p0,p1,p2; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t.ts, 2020-04-15 00:00:00.001000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4292,7 +4292,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo explain select * from t where ts < '2020-04-15 00:00:00' -- expect perfect : p0,p1, obtain: p0,p1,p2; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t.ts, 2020-04-15 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4304,7 +4304,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo explain select * from t where ts < '2020-04-14 23:59:59.999' -- p0,p1; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] lt(test.t.ts, 2020-04-14 23:59:59.999000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4318,7 +4318,7 @@ TableReader_8 3323.33 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo explain select * from t where ts < '2021-05-03 00:00:00' -- p0,p1,p2; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] lt(test.t.ts, 2021-05-03 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4330,7 +4330,7 @@ Union_9 9970.00 root └─TableFullScan_16 10000.00 cop[tikv] table:t, partition:p2 keep order:false, stats:pseudo explain select * from t where ts <= '2020-04-05 00:00:00' -- p0,p1; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t.ts, 2020-04-05 00:00:00.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4344,7 +4344,7 @@ TableReader_8 3323.33 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo explain select * from t where ts <= '2020-04-14 23:59:59.123' -- p0,p1; id estRows task access object operator info -Union_8 6646.67 root +PartitionUnion_8 6646.67 root ├─TableReader_11 3323.33 root data:Selection_10 │ └─Selection_10 3323.33 cop[tikv] le(test.t.ts, 2020-04-14 23:59:59.123000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4353,7 +4353,7 @@ Union_8 6646.67 root └─TableFullScan_12 10000.00 cop[tikv] table:t, partition:p1 keep order:false, stats:pseudo explain select * from t where ts <= '2020-04-25 00:00:00' -- p0,p1,p2; id estRows task access object operator info -Union_9 9970.00 root +PartitionUnion_9 9970.00 root ├─TableReader_12 3323.33 root data:Selection_11 │ └─Selection_11 3323.33 cop[tikv] le(test.t.ts, 2020-04-25 00:00:00.000000) │ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo @@ -4370,7 +4370,7 @@ TableReader_8 6656.67 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo explain select * from t where ts > '2020-04-02 00:00:00' and ts < '2020-04-07 00:00:00' -- p0,p1; id estRows task access object operator info -Union_8 500.00 root +PartitionUnion_8 500.00 root ├─TableReader_11 250.00 root data:Selection_10 │ └─Selection_10 250.00 cop[tikv] gt(test.t.ts, 2020-04-02 00:00:00.000000), lt(test.t.ts, 2020-04-07 00:00:00.000000) │ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo diff --git a/cmd/explaintest/r/select.result b/cmd/explaintest/r/select.result index 20f728664831f..f214a443ce781 100644 --- a/cmd/explaintest/r/select.result +++ b/cmd/explaintest/r/select.result @@ -364,7 +364,7 @@ TableReader_8 10.00 root data:Selection_7 └─TableFullScan_6 10000.00 cop[tikv] table:th, partition:p2 keep order:false, stats:pseudo desc select * from th; id estRows task access object operator info -Union_8 30000.00 root +PartitionUnion_8 30000.00 root ├─TableReader_10 10000.00 root data:TableFullScan_9 │ └─TableFullScan_9 10000.00 cop[tikv] table:th, partition:p0 keep order:false, stats:pseudo ├─TableReader_12 10000.00 root data:TableFullScan_11 @@ -373,7 +373,7 @@ Union_8 30000.00 root └─TableFullScan_13 10000.00 cop[tikv] table:th, partition:p2 keep order:false, stats:pseudo desc select * from th partition (p2,p1); id estRows task access object operator info -Union_7 20000.00 root +PartitionUnion_7 20000.00 root ├─TableReader_9 10000.00 root data:TableFullScan_8 │ └─TableFullScan_8 10000.00 cop[tikv] table:th, partition:p1 keep order:false, stats:pseudo └─TableReader_11 10000.00 root data:TableFullScan_10 diff --git a/expression/testdata/partition_pruner_out.json b/expression/testdata/partition_pruner_out.json index 9061476013d43..7ef3d5de72524 100644 --- a/expression/testdata/partition_pruner_out.json +++ b/expression/testdata/partition_pruner_out.json @@ -102,7 +102,7 @@ { "SQL": "explain select * from t6 where b is null", "Result": [ - "Union_9 30.00 root ", + "PartitionUnion_9 30.00 root ", "├─TableReader_12 10.00 root data:Selection_11", "│ └─Selection_11 10.00 cop[tikv] isnull(test_partition.t6.b)", "│ └─TableFullScan_10 10000.00 cop[tikv] table:t6, partition:p0 keep order:false, stats:pseudo", diff --git a/planner/core/exhaust_physical_plans.go b/planner/core/exhaust_physical_plans.go index 8ead7e3844164..34e3259bbb19b 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/planner/core/exhaust_physical_plans.go @@ -32,6 +32,7 @@ import ( "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/util/plancodec" "github.com/pingcap/tidb/util/ranger" "github.com/pingcap/tidb/util/set" "go.uber.org/zap" @@ -1830,6 +1831,14 @@ func (p *LogicalUnionAll) exhaustPhysicalPlans(prop *property.PhysicalProperty) return []PhysicalPlan{ua}, true } +func (p *LogicalPartitionUnionAll) exhaustPhysicalPlans(prop *property.PhysicalProperty) ([]PhysicalPlan, bool) { + uas, flagHint := p.LogicalUnionAll.exhaustPhysicalPlans(prop) + for _, ua := range uas { + ua.(*PhysicalUnionAll).tp = plancodec.TypePartitionUnion + } + return uas, flagHint +} + func (ls *LogicalSort) getPhysicalSort(prop *property.PhysicalProperty) *PhysicalSort { ps := PhysicalSort{ByItems: ls.ByItems}.Init(ls.ctx, ls.stats.ScaleByExpectCnt(prop.ExpectedCnt), ls.blockOffset, &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64}) return ps diff --git a/planner/core/initialize.go b/planner/core/initialize.go index 620a99438deee..b7e488a4510a7 100644 --- a/planner/core/initialize.go +++ b/planner/core/initialize.go @@ -103,6 +103,12 @@ func (p LogicalUnionAll) Init(ctx sessionctx.Context, offset int) *LogicalUnionA return &p } +// Init initializes LogicalPartitionUnionAll. +func (p LogicalPartitionUnionAll) Init(ctx sessionctx.Context, offset int) *LogicalPartitionUnionAll { + p.baseLogicalPlan = newBaseLogicalPlan(ctx, plancodec.TypePartitionUnion, &p, offset) + return &p +} + // Init initializes PhysicalUnionAll. func (p PhysicalUnionAll) Init(ctx sessionctx.Context, stats *property.StatsInfo, offset int, props ...*property.PhysicalProperty) *PhysicalUnionAll { p.basePhysicalPlan = newBasePhysicalPlan(ctx, plancodec.TypeUnion, &p, offset) diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index 236bbf1a682ec..1f8fd59e7c2ec 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -880,6 +880,11 @@ type LogicalUnionAll struct { logicalSchemaProducer } +// LogicalPartitionUnionAll represents the LogicalUnionAll plan is for partition table. +type LogicalPartitionUnionAll struct { + LogicalUnionAll +} + // LogicalSort stands for the order by plan. type LogicalSort struct { baseLogicalPlan diff --git a/planner/core/rule_aggregation_push_down.go b/planner/core/rule_aggregation_push_down.go index dc44480da4362..a3c76f43a2ef0 100644 --- a/planner/core/rule_aggregation_push_down.go +++ b/planner/core/rule_aggregation_push_down.go @@ -366,12 +366,29 @@ func (a *aggregationPushDownSolver) pushAggCrossUnion(agg *LogicalAggregation, u } func (a *aggregationPushDownSolver) optimize(ctx context.Context, p LogicalPlan) (LogicalPlan, error) { - if !p.SCtx().GetSessionVars().AllowAggPushDown { - return p, nil - } return a.aggPushDown(p) } +func (a *aggregationPushDownSolver) tryAggPushDownForUnion(union *LogicalUnionAll, agg *LogicalAggregation) error { + for _, aggFunc := range agg.AggFuncs { + if !a.isDecomposableWithUnion(aggFunc) { + return nil + } + } + pushedAgg := a.splitPartialAgg(agg) + newChildren := make([]LogicalPlan, 0, len(union.Children())) + for _, child := range union.Children() { + newChild, err := a.pushAggCrossUnion(pushedAgg, union.Schema(), child) + if err != nil { + return err + } + newChildren = append(newChildren, newChild) + } + union.SetSchema(expression.NewSchema(newChildren[0].Schema().Columns...)) + union.SetChildren(newChildren...) + return nil +} + // aggPushDown tries to push down aggregate functions to join paths. func (a *aggregationPushDownSolver) aggPushDown(p LogicalPlan) (_ LogicalPlan, err error) { if agg, ok := p.(*LogicalAggregation); ok { @@ -380,7 +397,7 @@ func (a *aggregationPushDownSolver) aggPushDown(p LogicalPlan) (_ LogicalPlan, e p = proj } else { child := agg.children[0] - if join, ok1 := child.(*LogicalJoin); ok1 && a.checkValidJoin(join) { + if join, ok1 := child.(*LogicalJoin); ok1 && a.checkValidJoin(join) && p.SCtx().GetSessionVars().AllowAggPushDown { if valid, leftAggFuncs, rightAggFuncs, leftGbyCols, rightGbyCols := a.splitAggFuncsAndGbyCols(agg, join); valid { var lChild, rChild LogicalPlan // If there exist count or sum functions in left join path, we can't push any @@ -411,7 +428,7 @@ func (a *aggregationPushDownSolver) aggPushDown(p LogicalPlan) (_ LogicalPlan, e p = proj } } - } else if proj, ok1 := child.(*LogicalProjection); ok1 { + } else if proj, ok1 := child.(*LogicalProjection); ok1 && p.SCtx().GetSessionVars().AllowAggPushDown { // TODO: This optimization is not always reasonable. We have not supported pushing projection to kv layer yet, // so we must do this optimization. for i, gbyItem := range agg.GroupByItems { @@ -427,23 +444,16 @@ func (a *aggregationPushDownSolver) aggPushDown(p LogicalPlan) (_ LogicalPlan, e } projChild := proj.children[0] agg.SetChildren(projChild) - } else if union, ok1 := child.(*LogicalUnionAll); ok1 { - for _, aggFunc := range agg.AggFuncs { - if !a.isDecomposableWithUnion(aggFunc) { - return p, nil - } + } else if union, ok1 := child.(*LogicalUnionAll); ok1 && p.SCtx().GetSessionVars().AllowAggPushDown { + err := a.tryAggPushDownForUnion(union, agg) + if err != nil { + return nil, err } - pushedAgg := a.splitPartialAgg(agg) - newChildren := make([]LogicalPlan, 0, len(union.children)) - for _, child := range union.children { - newChild, err := a.pushAggCrossUnion(pushedAgg, union.Schema(), child) - if err != nil { - return p, err - } - newChildren = append(newChildren, newChild) + } else if union, ok1 := child.(*LogicalPartitionUnionAll); ok1 { + err := a.tryAggPushDownForUnion(&union.LogicalUnionAll, agg) + if err != nil { + return nil, err } - union.SetSchema(expression.NewSchema(newChildren[0].Schema().Columns...)) - union.SetChildren(newChildren...) } } } diff --git a/planner/core/rule_partition_processor.go b/planner/core/rule_partition_processor.go index be3014ac0bafc..81d89fdf91129 100644 --- a/planner/core/rule_partition_processor.go +++ b/planner/core/rule_partition_processor.go @@ -61,7 +61,7 @@ func (s *partitionProcessor) rewriteDataSource(lp LogicalPlan) (LogicalPlan, err if err != nil { return nil, err } - if ua, ok := ds.(*LogicalUnionAll); ok { + if ua, ok := ds.(*LogicalPartitionUnionAll); ok { // Adjust the UnionScan->Union->DataSource1, DataSource2 ... to // Union->(UnionScan->DataSource1), (UnionScan->DataSource2) children := make([]LogicalPlan, 0, len(ua.Children())) @@ -733,7 +733,7 @@ func (s *partitionProcessor) makeUnionAllChildren(ds *DataSource, pi *model.Part // No need for the union all. return children[0], nil } - unionAll := LogicalUnionAll{}.Init(ds.SCtx(), ds.blockOffset) + unionAll := LogicalPartitionUnionAll{}.Init(ds.SCtx(), ds.blockOffset) unionAll.SetChildren(children...) unionAll.SetSchema(ds.schema.Clone()) return unionAll, nil diff --git a/planner/core/stringer.go b/planner/core/stringer.go index ce451ff16da50..6dcc3326cefef 100644 --- a/planner/core/stringer.go +++ b/planner/core/stringer.go @@ -17,6 +17,8 @@ import ( "bytes" "fmt" "strings" + + "github.com/pingcap/tidb/util/plancodec" ) // ToString explains a Plan, returns description string. @@ -131,12 +133,16 @@ func toString(in Plan, strs []string, idxs []int) ([]string, []int) { r := eq.GetArgs()[1].String() str += fmt.Sprintf("(%s,%s)", l, r) } - case *LogicalUnionAll, *PhysicalUnionAll: + case *LogicalUnionAll, *PhysicalUnionAll, *LogicalPartitionUnionAll: last := len(idxs) - 1 idx := idxs[last] children := strs[idx:] strs = strs[:idx] - str = "UnionAll{" + strings.Join(children, "->") + "}" + name := "UnionAll" + if x.TP() == plancodec.TypePartitionUnion { + name = "PartitionUnionAll" + } + str = name + "{" + strings.Join(children, "->") + "}" idxs = idxs[:last] case *DataSource: if x.isPartition { diff --git a/planner/core/testdata/analyze_suite_out.json b/planner/core/testdata/analyze_suite_out.json index c06467f2d840e..71f5f91f0a6d0 100644 --- a/planner/core/testdata/analyze_suite_out.json +++ b/planner/core/testdata/analyze_suite_out.json @@ -336,8 +336,8 @@ "IndexLookUp(Index(t1.a)[[1,1]], Table(t1)->Sel([le(test.t1.b, 2)]))", "TableReader(Table(t2)->Sel([le(test.t2.a, 2)]))", "Analyze{Index(a),Index(b)}", - "UnionAll{TableReader(Table(t4)->Sel([le(test.t4.a, 2)]))->TableReader(Table(t4)->Sel([le(test.t4.a, 2)]))}", - "UnionAll{IndexReader(Index(t4.b)[[-inf,2)])->IndexReader(Index(t4.b)[[-inf,2)])}", + "PartitionUnionAll{TableReader(Table(t4)->Sel([le(test.t4.a, 2)]))->TableReader(Table(t4)->Sel([le(test.t4.a, 2)]))}", + "PartitionUnionAll{IndexReader(Index(t4.b)[[-inf,2)])->IndexReader(Index(t4.b)[[-inf,2)])}", "TableReader(Table(t4)->Sel([eq(test.t4.a, 1) le(test.t4.b, 2)]))" ] } diff --git a/planner/core/testdata/integration_suite_out.json b/planner/core/testdata/integration_suite_out.json index 85dc39cf097d8..40aae7d519dbe 100644 --- a/planner/core/testdata/integration_suite_out.json +++ b/planner/core/testdata/integration_suite_out.json @@ -145,7 +145,7 @@ "SQL": "explain select * from t order by a", "Result": [ "Sort_8 10005.00 root test.t.a", - "└─Union_11 10005.00 root ", + "└─PartitionUnion_11 10005.00 root ", " ├─TableReader_13 10000.00 root data:TableFullScan_12", " │ └─TableFullScan_12 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", " ├─TableReader_15 1.00 root data:TableFullScan_14", @@ -168,7 +168,7 @@ "SQL": "explain select * from t order by a limit 3", "Result": [ "TopN_16 3.00 root test.t.a, offset:0, count:3", - "└─Union_20 7.00 root ", + "└─PartitionUnion_20 7.00 root ", " ├─TopN_21 3.00 root test.t.a, offset:0, count:3", " │ └─TableReader_29 3.00 root data:TopN_28", " │ └─TopN_28 3.00 cop[tikv] test.t.a, offset:0, count:3", @@ -336,12 +336,11 @@ { "SQL": "desc select /*+ INL_HASH_JOIN(t2) */ distinct t2.a from t t1 join t t2 on t1.a = t2.a", "Plan": [ - "HashAgg_8 8000.00 root group by:test.t.a, funcs:firstrow(test.t.a)->test.t.a", - "└─IndexHashJoin_20 12500.00 root inner join, inner:TableReader_13, outer key:test.t.a, inner key:test.t.a", - " ├─TableReader_22(Build) 10000.00 root data:TableFullScan_21", - " │ └─TableFullScan_21 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", - " └─TableReader_13(Probe) 1.00 root data:TableRangeScan_12", - " └─TableRangeScan_12 1.00 cop[tikv] table:t2 range: decided by [test.t.a], keep order:false, stats:pseudo" + "IndexHashJoin_17 12500.00 root inner join, inner:TableReader_10, outer key:test.t.a, inner key:test.t.a", + "├─TableReader_19(Build) 10000.00 root data:TableFullScan_18", + "│ └─TableFullScan_18 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo", + "└─TableReader_10(Probe) 1.00 root data:TableRangeScan_9", + " └─TableRangeScan_9 1.00 cop[tikv] table:t2 range: decided by [test.t.a], keep order:false, stats:pseudo" ], "Warnings": [] }, @@ -507,7 +506,7 @@ { "SQL": "explain select * from t where a in (1, 2,'11')", "Plan": [ - "Union_8 60.00 root ", + "PartitionUnion_8 60.00 root ", "├─TableReader_11 30.00 root data:Selection_10", "│ └─Selection_10 30.00 cop[tikv] in(test.t.a, 1, 2, 11)", "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", @@ -519,7 +518,7 @@ { "SQL": "explain select * from t where a in (17, null)", "Plan": [ - "Union_8 20.00 root ", + "PartitionUnion_8 20.00 root ", "├─TableReader_11 10.00 root data:Selection_10", "│ └─Selection_10 10.00 cop[tikv] in(test.t.a, 17, NULL)", "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", @@ -531,7 +530,7 @@ { "SQL": "explain select * from t where a in (16, 'abc')", "Plan": [ - "Union_8 40.00 root ", + "PartitionUnion_8 40.00 root ", "├─TableReader_11 20.00 root data:Selection_10", "│ └─Selection_10 20.00 cop[tikv] in(test.t.a, 16, 0)", "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", @@ -557,7 +556,7 @@ { "SQL": "explain select * from t where a in (14, floor(3.47))", "Plan": [ - "Union_8 40.00 root ", + "PartitionUnion_8 40.00 root ", "├─TableReader_11 20.00 root data:Selection_10", "│ └─Selection_10 20.00 cop[tikv] in(test.t.a, 14, 3)", "│ └─TableFullScan_9 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", @@ -569,7 +568,7 @@ { "SQL": "explain select * from t where b in (3, 4)", "Plan": [ - "Union_9 60.00 root ", + "PartitionUnion_9 60.00 root ", "├─TableReader_12 20.00 root data:Selection_11", "│ └─Selection_11 20.00 cop[tikv] in(test.t.b, 3, 4)", "│ └─TableFullScan_10 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", diff --git a/planner/core/testdata/plan_suite_out.json b/planner/core/testdata/plan_suite_out.json index d0b9626f8d13e..47b210705fb2d 100644 --- a/planner/core/testdata/plan_suite_out.json +++ b/planner/core/testdata/plan_suite_out.json @@ -607,11 +607,11 @@ }, { "SQL": "select (select count(*) from t s, t t1 where s.a = t.a and s.a = t1.a) from t", - "Best": "LeftHashJoin{IndexReader(Index(t.f)[[NULL,+inf]])->LeftHashJoin{IndexReader(Index(t.f)[[NULL,+inf]])->IndexReader(Index(t.f)[[NULL,+inf]])}(test.t.a,test.t.a)->HashAgg}(test.t.a,test.t.a)->Projection" + "Best": "LeftHashJoin{IndexReader(Index(t.f)[[NULL,+inf]])->LeftHashJoin{IndexReader(Index(t.f)[[NULL,+inf]])->IndexReader(Index(t.f)[[NULL,+inf]])}(test.t.a,test.t.a)->Projection}(test.t.a,test.t.a)->Projection" }, { "SQL": "select (select count(*) from t s, t t1 where s.a = t.a and s.a = t1.a) from t order by t.a", - "Best": "MergeLeftOuterJoin{TableReader(Table(t))->MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->StreamAgg}(test.t.a,test.t.a)->Projection->Projection" + "Best": "MergeLeftOuterJoin{TableReader(Table(t))->MergeInnerJoin{TableReader(Table(t))->TableReader(Table(t))}(test.t.a,test.t.a)->Projection}(test.t.a,test.t.a)->Projection->Projection" } ] }, @@ -1220,7 +1220,7 @@ }, { "SQL": "select /*+ STREAM_AGG() */ distinct a from t", - "Best": "TableReader(Table(t))->StreamAgg", + "Best": "IndexReader(Index(t.f)[[NULL,+inf]])", "Warning": "" }, { @@ -1367,14 +1367,14 @@ ], "Plan": [ "HashJoin_19 25000.00 root inner join, equal:[eq(test.tt.a, test.tt.a)]", - "├─Union_28(Build) 20000.00 root ", + "├─PartitionUnion_28(Build) 20000.00 root ", "│ ├─UnionScan_29 10000.00 root ", "│ │ └─TableReader_31 10000.00 root data:TableFullScan_30", "│ │ └─TableFullScan_30 10000.00 cop[tikv] table:t2, partition:p0 keep order:false, stats:pseudo", "│ └─UnionScan_32 10000.00 root ", "│ └─TableReader_34 10000.00 root data:TableFullScan_33", "│ └─TableFullScan_33 10000.00 cop[tikv] table:t2, partition:p1 keep order:false, stats:pseudo", - "└─Union_21(Probe) 20000.00 root ", + "└─PartitionUnion_21(Probe) 20000.00 root ", " ├─UnionScan_22 10000.00 root ", " │ └─TableReader_24 10000.00 root data:TableFullScan_23", " │ └─TableFullScan_23 10000.00 cop[tikv] table:t1, partition:p0 keep order:false, stats:pseudo", @@ -1556,7 +1556,7 @@ "Plan": [ "HashAgg_11 1.00 root funcs:sum(distinct Column#9)->Column#4", "└─Projection_23 16000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#9", - " └─Union_12 16000.00 root ", + " └─PartitionUnion_12 16000.00 root ", " ├─HashAgg_16 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", " │ └─TableReader_17 8000.00 root data:HashAgg_13", " │ └─HashAgg_13 8000.00 cop[tikv] group by:test.pt.b, ", @@ -1685,7 +1685,7 @@ "Plan": [ "HashAgg_11 1.00 root funcs:sum(distinct Column#9)->Column#4", "└─Projection_23 16000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#9", - " └─Union_12 16000.00 root ", + " └─PartitionUnion_12 16000.00 root ", " ├─HashAgg_16 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", " │ └─TableReader_17 8000.00 root data:HashAgg_13", " │ └─HashAgg_13 8000.00 cop[tikv] group by:test.pt.b, ", @@ -1725,13 +1725,17 @@ { "SQL": "select /*+ HASH_AGG(), AGG_TO_COP() */ sum(distinct b) from pt;", "Plan": [ - "HashAgg_8 1.00 root funcs:sum(distinct Column#5)->Column#4", - "└─Projection_14 20000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#5", - " └─Union_9 20000.00 root ", - " ├─TableReader_11 10000.00 root data:TableFullScan_10", - " │ └─TableFullScan_10 10000.00 cop[tikv] table:pt, partition:p0 keep order:false, stats:pseudo", - " └─TableReader_13 10000.00 root data:TableFullScan_12", - " └─TableFullScan_12 10000.00 cop[tikv] table:pt, partition:p1 keep order:false, stats:pseudo" + "HashAgg_11 1.00 root funcs:sum(distinct Column#9)->Column#4", + "└─Projection_23 16000.00 root cast(test.pt.b, decimal(65,0) BINARY)->Column#9", + " └─PartitionUnion_12 16000.00 root ", + " ├─HashAgg_16 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", + " │ └─TableReader_17 8000.00 root data:HashAgg_13", + " │ └─HashAgg_13 8000.00 cop[tikv] group by:test.pt.b, ", + " │ └─TableFullScan_15 10000.00 cop[tikv] table:pt, partition:p0 keep order:false, stats:pseudo", + " └─HashAgg_21 8000.00 root group by:test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b, funcs:firstrow(test.pt.b)->test.pt.b", + " └─TableReader_22 8000.00 root data:HashAgg_18", + " └─HashAgg_18 8000.00 cop[tikv] group by:test.pt.b, ", + " └─TableFullScan_20 10000.00 cop[tikv] table:pt, partition:p1 keep order:false, stats:pseudo" ], "Result": [ "" @@ -1773,7 +1777,7 @@ "Plan": [ "HashAgg_10 1.00 root funcs:group_concat(Column#6 order by Column#7 desc separator \"++\")->Column#4, funcs:group_concat(Column#8 order by Column#9 desc, Column#10 separator \"--\")->Column#5", "└─Projection_23 20000.00 root cast(test.ptest.name, var_string(20))->Column#6, test.ptest.name, cast(test.ptest.id, var_string(20))->Column#8, test.ptest.name, test.ptest.id", - " └─Union_13 20000.00 root ", + " └─PartitionUnion_13 20000.00 root ", " ├─TableReader_15 10000.00 root data:TableFullScan_14", " │ └─TableFullScan_14 10000.00 cop[tikv] table:ptest, partition:p0 keep order:false, stats:pseudo", " └─TableReader_17 10000.00 root data:TableFullScan_16", @@ -1800,7 +1804,7 @@ "Plan": [ "StreamAgg_9 1.00 root funcs:group_concat(distinct Column#5 order by Column#6 desc separator \",\")->Column#4", "└─Projection_20 20000.00 root cast(test.ptest.name, var_string(20))->Column#5, test.ptest.name", - " └─Union_15 20000.00 root ", + " └─PartitionUnion_15 20000.00 root ", " ├─TableReader_17 10000.00 root data:TableFullScan_16", " │ └─TableFullScan_16 10000.00 cop[tikv] table:ptest, partition:p0 keep order:false, stats:pseudo", " └─TableReader_19 10000.00 root data:TableFullScan_18", diff --git a/planner/core/testdata/plan_suite_unexported_out.json b/planner/core/testdata/plan_suite_unexported_out.json index 490b04077f4a0..1e6eee59a3bb2 100644 --- a/planner/core/testdata/plan_suite_unexported_out.json +++ b/planner/core/testdata/plan_suite_unexported_out.json @@ -809,17 +809,17 @@ { "Name": "TestTablePartition", "Cases": [ - "UnionAll{Partition(41)->Partition(42)->Partition(43)->Partition(44)->Partition(45)}->Projection", - "UnionAll{Partition(41)->Partition(42)}->Projection", - "UnionAll{Partition(41)->Partition(42)->Partition(43)}->Projection", - "UnionAll{Partition(42)->Partition(43)}->Projection", - "UnionAll{UnionAll{Partition(42)->Partition(43)}->Projection->Projection->UnionAll{Partition(42)->Partition(43)}->Projection->Projection}", - "UnionAll{UnionAll{Partition(42)->Partition(43)}->Projection->Projection->UnionAll{Partition(42)->Partition(43)}->Projection->Projection}", + "PartitionUnionAll{Partition(41)->Partition(42)->Partition(43)->Partition(44)->Partition(45)}->Projection", + "PartitionUnionAll{Partition(41)->Partition(42)}->Projection", + "PartitionUnionAll{Partition(41)->Partition(42)->Partition(43)}->Projection", + "PartitionUnionAll{Partition(42)->Partition(43)}->Projection", + "UnionAll{PartitionUnionAll{Partition(42)->Partition(43)}->Projection->Projection->PartitionUnionAll{Partition(42)->Partition(43)}->Projection->Projection}", + "UnionAll{PartitionUnionAll{Partition(42)->Partition(43)}->Projection->Projection->PartitionUnionAll{Partition(42)->Partition(43)}->Projection->Projection}", "Partition(41)->Projection", "Partition(45)->Projection", "Dual->Projection", "Partition(41)->Projection", - "UnionAll{Partition(41)->Partition(44)}->Projection" + "PartitionUnionAll{Partition(41)->Partition(44)}->Projection" ] }, { @@ -897,8 +897,8 @@ { "Name": "TestOuterJoinEliminator", "Cases": [ - "DataScan(t1)->Aggr(firstrow(test.t.a),firstrow(test.t.b))", - "DataScan(t2)->Aggr(firstrow(test.t.a),firstrow(test.t.b))", + "DataScan(t1)->Projection", + "DataScan(t2)->Projection", "DataScan(t1)->Aggr(max(test.t.a),min(test.t.b))->Projection", "DataScan(t1)->Aggr(sum(distinct test.t.a))->Projection", "DataScan(t1)->Aggr(count(distinct test.t.a, test.t.b))->Projection", diff --git a/util/plancodec/id.go b/util/plancodec/id.go index d4fb2828703a5..b460bf86e70ec 100644 --- a/util/plancodec/id.go +++ b/util/plancodec/id.go @@ -34,6 +34,8 @@ const ( TypeJoin = "Join" // TypeUnion is the type of Union. TypeUnion = "Union" + // TypePartitionUnion is the type of PartitionUnion + TypePartitionUnion = "PartitionUnion" // TypeTableScan is the type of TableScan. TypeTableScan = "TableScan" // TypeMemTableScan is the type of TableScan. From f1b21b562a1b2bef0740780665a481876fdb843a Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Fri, 29 May 2020 05:05:35 +0200 Subject: [PATCH 61/74] types: match MySQL behavior with datetime delimiters (#17376) --- expression/builtin_time_test.go | 55 +++++++++++++++++++- expression/integration_test.go | 2 +- types/helper.go | 5 ++ types/time.go | 53 ++++++++++++------- types/time_test.go | 90 ++++++++++++++++++++++++++++++--- 5 files changed, 177 insertions(+), 28 deletions(-) diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 575b354faef9c..b723b689a0bf0 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -47,9 +47,60 @@ func (s *testEvaluatorSuite) TestDate(c *C) { Input interface{} Expect interface{} }{ - {"2011-11-11", "2011-11-11"}, {nil, nil}, - {"2011-11-11 10:10:10", "2011-11-11"}, + // standard format + {"2011-12-13", "2011-12-13"}, + {"2011-12-13 10:10:10", "2011-12-13"}, + // alternative delimiters, any ASCII punctuation character is a valid delimiter, + // punctuation character is defined by C++ std::ispunct: any graphical character + // that is not alphanumeric. + {"2011\"12\"13", "2011-12-13"}, + {"2011#12#13", "2011-12-13"}, + {"2011$12$13", "2011-12-13"}, + {"2011%12%13", "2011-12-13"}, + {"2011&12&13", "2011-12-13"}, + {"2011'12'13", "2011-12-13"}, + {"2011(12(13", "2011-12-13"}, + {"2011)12)13", "2011-12-13"}, + {"2011*12*13", "2011-12-13"}, + {"2011+12+13", "2011-12-13"}, + {"2011,12,13", "2011-12-13"}, + {"2011.12.13", "2011-12-13"}, + {"2011/12/13", "2011-12-13"}, + {"2011:12:13", "2011-12-13"}, + {"2011;12;13", "2011-12-13"}, + {"2011<12<13", "2011-12-13"}, + {"2011=12=13", "2011-12-13"}, + {"2011>12>13", "2011-12-13"}, + {"2011?12?13", "2011-12-13"}, + {"2011@12@13", "2011-12-13"}, + {"2011[12[13", "2011-12-13"}, + {"2011\\12\\13", "2011-12-13"}, + {"2011]12]13", "2011-12-13"}, + {"2011^12^13", "2011-12-13"}, + {"2011_12_13", "2011-12-13"}, + {"2011`12`13", "2011-12-13"}, + {"2011{12{13", "2011-12-13"}, + {"2011|12|13", "2011-12-13"}, + {"2011}12}13", "2011-12-13"}, + {"2011~12~13", "2011-12-13"}, + // internal format (YYYYMMDD, YYYYYMMDDHHMMSS) + {"20111213", "2011-12-13"}, + {"111213", "2011-12-13"}, + // leading and trailing space + {" 2011-12-13", "2011-12-13"}, + {"2011-12-13 ", "2011-12-13"}, + {" 2011-12-13 ", "2011-12-13"}, + // extra dashes + {"2011-12--13", "2011-12-13"}, + {"2011--12-13", "2011-12-13"}, + {"2011----12----13", "2011-12-13"}, + // combinations + {" 2011----12----13 ", "2011-12-13"}, + // errors + {"2011 12 13", nil}, + {"2011A12A13", nil}, + {"2011T12T13", nil}, } dtblDate := tblToDtbl(tblDate) for _, t := range dtblDate { diff --git a/expression/integration_test.go b/expression/integration_test.go index 4c8f355b4c3e7..7b11b5b0635ae 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1442,7 +1442,7 @@ func (s *testIntegrationSuite2) TestTimeBuiltin(c *C) { result.Check(testkit.Rows("00:00:01")) tk.MustExec("drop table if exists t") tk.MustExec("create table t(a datetime, b timestamp, c time)") - tk.MustExec(`insert into t values("2017 01-01 12:30:31", "2017 01-01 12:30:31", "01:01:01")`) + tk.MustExec(`insert into t values("2017-01-01 12:30:31", "2017-01-01 12:30:31", "01:01:01")`) result = tk.MustQuery("select addtime(a, b), addtime(cast(a as date), b), addtime(b,a), addtime(a,c), addtime(b," + "c), addtime(c,a), addtime(c,b)" + " from t;") diff --git a/types/helper.go b/types/helper.go index 917afe756e492..2c8d4b3c8f9f8 100644 --- a/types/helper.go +++ b/types/helper.go @@ -105,6 +105,11 @@ func isDigit(c byte) bool { return c >= '0' && c <= '9' } +// Returns true if the given byte is an ASCII punctuation character (printable and non-alphanumeric). +func isPunctuation(c byte) bool { + return (c >= 0x21 && c <= 0x2F) || (c >= 0x3A && c <= 0x40) || (c >= 0x5B && c <= 0x60) || (c >= 0x7B && c <= 0x7E) +} + func myMax(a, b int) int { if a > b { return a diff --git a/types/time.go b/types/time.go index 8674ab4c505cb..f6c43d9b309b2 100644 --- a/types/time.go +++ b/types/time.go @@ -735,38 +735,60 @@ func TimestampDiff(unit string, t1 Time, t2 Time) int64 { func ParseDateFormat(format string) []string { format = strings.TrimSpace(format) + if len(format) == 0 { + return nil + } + + // Date format must start and end with number. + if !isDigit(format[0]) || !isDigit(format[len(format)-1]) { + return nil + } + start := 0 // Initialize `seps` with capacity of 6. The input `format` is typically // a date time of the form "2006-01-02 15:04:05", which has 6 numeric parts // (the fractional second part is usually removed by `splitDateTime`). // Setting `seps`'s capacity to 6 avoids reallocation in this common case. seps := make([]string, 0, 6) - for i := 0; i < len(format); i++ { - // Date format must start and end with number. - if i == 0 || i == len(format)-1 { - if !unicode.IsNumber(rune(format[i])) { - return nil - } - continue - } + for i := 1; i < len(format)-1; i++ { + if isValidSeparator(format[i], len(seps)) { + prevParts := len(seps) + seps = append(seps, format[start:i]) + start = i + 1 - // Separator is a single none-number char. - if !unicode.IsNumber(rune(format[i])) { - if !unicode.IsNumber(rune(format[i-1])) { - return nil + // consume further consecutive separators + for j := i + 1; j < len(format); j++ { + if !isValidSeparator(format[j], prevParts) { + break + } + + start++ + i++ } - seps = append(seps, format[start:i]) - start = i + 1 + continue } + if !isDigit(format[i]) { + return nil + } } seps = append(seps, format[start:]) return seps } +// helper for date part splitting, punctuation characters are valid separators anywhere, +// while space and 'T' are valid separators only between date and time. +func isValidSeparator(c byte, prevParts int) bool { + if isPunctuation(c) { + return true + } + + return prevParts == 2 && (c == ' ' || c == 'T') +} + // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html. // The only delimiter recognized between a date and time part and a fractional seconds part is the decimal point. func splitDateTime(format string) (seps []string, fracStr string) { @@ -782,9 +804,6 @@ func splitDateTime(format string) (seps []string, fracStr string) { // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html. func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int8, isFloat bool) (Time, error) { - // Try to split str with delimiter. - // TODO: only punctuation can be the delimiter for date parts or time parts. - // But only space and T can be the delimiter between the date and time part. var ( year, month, day, hour, minute, second int fracStr string diff --git a/types/time_test.go b/types/time_test.go index cb42918fc64cd..063a7fdf05b18 100644 --- a/types/time_test.go +++ b/types/time_test.go @@ -191,15 +191,65 @@ func (s *testTimeSuite) TestDate(c *C) { Input string Expect string }{ - {"2012-12-31", "2012-12-31"}, - {"00-12-31", "2000-12-31"}, - {"20121231", "2012-12-31"}, - {"121231", "2012-12-31"}, + // standard format + {"0001-12-13", "0001-12-13"}, + {"2011-12-13", "2011-12-13"}, + {"2011-12-13 10:10:10", "2011-12-13"}, {"2015-06-01 12:12:12", "2015-06-01"}, {"0001-01-01 00:00:00", "0001-01-01"}, - {"0001-01-01", "0001-01-01"}, - {"2019.01.01", "2019-01-01"}, - {"2019/01/01", "2019-01-01"}, + // 2-digit year + {"00-12-31", "2000-12-31"}, + // alternative delimiters, any ASCII punctuation character is a valid delimiter, + // punctuation character is defined by C++ std::ispunct: any graphical character + // that is not alphanumeric. + {"2011\"12\"13", "2011-12-13"}, + {"2011#12#13", "2011-12-13"}, + {"2011$12$13", "2011-12-13"}, + {"2011%12%13", "2011-12-13"}, + {"2011&12&13", "2011-12-13"}, + {"2011'12'13", "2011-12-13"}, + {"2011(12(13", "2011-12-13"}, + {"2011)12)13", "2011-12-13"}, + {"2011*12*13", "2011-12-13"}, + {"2011+12+13", "2011-12-13"}, + {"2011,12,13", "2011-12-13"}, + {"2011.12.13", "2011-12-13"}, + {"2011/12/13", "2011-12-13"}, + {"2011:12:13", "2011-12-13"}, + {"2011;12;13", "2011-12-13"}, + {"2011<12<13", "2011-12-13"}, + {"2011=12=13", "2011-12-13"}, + {"2011>12>13", "2011-12-13"}, + {"2011?12?13", "2011-12-13"}, + {"2011@12@13", "2011-12-13"}, + {"2011[12[13", "2011-12-13"}, + {"2011\\12\\13", "2011-12-13"}, + {"2011]12]13", "2011-12-13"}, + {"2011^12^13", "2011-12-13"}, + {"2011_12_13", "2011-12-13"}, + {"2011`12`13", "2011-12-13"}, + {"2011{12{13", "2011-12-13"}, + {"2011|12|13", "2011-12-13"}, + {"2011}12}13", "2011-12-13"}, + {"2011~12~13", "2011-12-13"}, + // alternative separators with time + {"2011~12~13 12~12~12", "2011-12-13"}, + {"2011~12~13T12~12~12", "2011-12-13"}, + {"2011~12~13~12~12~12", "2011-12-13"}, + // internal format (YYYYMMDD, YYYYYMMDDHHMMSS) + {"20111213", "2011-12-13"}, + {"111213", "2011-12-13"}, + // leading and trailing space + {" 2011-12-13", "2011-12-13"}, + {"2011-12-13 ", "2011-12-13"}, + {" 2011-12-13 ", "2011-12-13"}, + // extra separators + {"2011-12--13", "2011-12-13"}, + {"2011--12-13", "2011-12-13"}, + {"2011----12----13", "2011-12-13"}, + {"2011~/.12)_#13T T.12~)12[~12", "2011-12-13"}, + // combinations + {" 2011----12----13 ", "2011-12-13"}, } for _, test := range table { @@ -213,6 +263,13 @@ func (s *testTimeSuite) TestDate(c *C) { "1201012736.0000", "1201012736", "2019.01", + // invalid separators + "2019 01 02", + "2019A01A02", + "2019-01T02", + "2011-12-13 10:10T10", + "2019–01–02", // en dash + "2019—01—02", // em dash } for _, test := range errTable { @@ -957,10 +1014,10 @@ func (s *testTimeSuite) TestParseDateFormat(c *C) { {"2011-11-11 10", []string{"2011", "11", "11", "10"}}, {"2011-11-11T10:10:10.123456", []string{"2011", "11", "11", "10", "10", "10", "123456"}}, {"2011:11:11T10:10:10.123456", []string{"2011", "11", "11", "10", "10", "10", "123456"}}, + {"2011-11-11 10:10:10", []string{"2011", "11", "11", "10", "10", "10"}}, {"xx2011-11-11 10:10:10", nil}, {"T10:10:10", nil}, {"2011-11-11x", nil}, - {"2011-11-11 10:10:10", nil}, {"xxx 10:10:10", nil}, } @@ -1821,6 +1878,23 @@ func BenchmarkTimeCompare(b *testing.B) { } } +func benchmarkDateFormat(b *testing.B, name, str string) { + b.Run(name, func(b *testing.B) { + for i := 0; i < b.N; i++ { + types.ParseDateFormat(str) + } + }) +} + +func BenchmarkParseDateFormat(b *testing.B) { + benchmarkDateFormat(b, "date basic", "2011-12-13") + benchmarkDateFormat(b, "date internal", "20111213") + benchmarkDateFormat(b, "datetime basic", "2011-12-13 14:15:16") + benchmarkDateFormat(b, "datetime internal", "20111213141516") + benchmarkDateFormat(b, "datetime basic frac", "2011-12-13 14:15:16.123456") + benchmarkDateFormat(b, "datetime repeated delimiters", "2011---12---13 14::15::16..123456") +} + func BenchmarkParseDatetime(b *testing.B) { sc := &stmtctx.StatementContext{TimeZone: time.UTC} str := "2011-10-10 11:11:11.123456" From 73fe3ca4ded18d9de7d57965e875c88585de6708 Mon Sep 17 00:00:00 2001 From: crazycs Date: Fri, 29 May 2020 12:06:57 +0800 Subject: [PATCH 62/74] infoschema: refine some metric table comment (#17279) Signed-off-by: crazycs520 --- infoschema/metric_table_def.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/infoschema/metric_table_def.go b/infoschema/metric_table_def.go index c2453e770259a..f9ba0e21495dc 100644 --- a/infoschema/metric_table_def.go +++ b/infoschema/metric_table_def.go @@ -156,7 +156,7 @@ var MetricTableMap = map[string]MetricTableDef{ PromQL: "histogram_quantile($QUANTILE, sum(rate(tidb_server_get_token_duration_seconds_bucket{$LABEL_CONDITIONS}[$RANGE_DURATION])) by (le,instance))", Labels: []string{"instance"}, Quantile: 0.99, - Comment: " The quantile of Duration (us) for getting token, it should be small until concurrency limit is reached(second)", + Comment: " The quantile of Duration (us) for getting token, it should be small until concurrency limit is reached(microsecond)", }, "tidb_handshake_error_opm": { PromQL: "sum(increase(tidb_server_handshake_error_total{$LABEL_CONDITIONS}[$RANGE_DURATION])) by (instance)", @@ -2496,12 +2496,12 @@ var MetricTableMap = map[string]MetricTableDef{ "tidb_get_token_total_count": { PromQL: "sum(increase(tidb_server_get_token_duration_seconds_count{$LABEL_CONDITIONS}[$RANGE_DURATION])) by (instance)", Labels: []string{"instance"}, - Comment: "The total count of Duration (us) for getting token, it should be small until concurrency limit is reached(second)", + Comment: "The total count of Duration (us) for getting token, it should be small until concurrency limit is reached", }, "tidb_get_token_total_time": { PromQL: "sum(increase(tidb_server_get_token_duration_seconds_sum{$LABEL_CONDITIONS}[$RANGE_DURATION])) by (instance)", Labels: []string{"instance"}, - Comment: "The total time of Duration (us) for getting token, it should be small until concurrency limit is reached(second)", + Comment: "The total time of Duration (us) for getting token, it should be small until concurrency limit is reached(microsecond)", }, "tidb_kv_backoff_total_count": { PromQL: "sum(increase(tidb_tikvclient_backoff_seconds_count{$LABEL_CONDITIONS}[$RANGE_DURATION])) by (instance,type)", From 709691607daf167a75d4b950ef5b5a659d7bd743 Mon Sep 17 00:00:00 2001 From: mantuliu <240951888@qq.com> Date: Fri, 29 May 2020 19:07:34 +0800 Subject: [PATCH 63/74] expensivequery: fix the issue that max_exec_time hint doesn't work if it exceeds the expensive-threshold (#17359) --- server/conn_test.go | 12 ++++++++++++ util/expensivequery/expensivequery.go | 7 ++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/server/conn_test.go b/server/conn_test.go index d9259b17fb99a..c91eb40438763 100644 --- a/server/conn_test.go +++ b/server/conn_test.go @@ -32,6 +32,7 @@ import ( "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/store/mockstore" "github.com/pingcap/tidb/util/arena" + "github.com/pingcap/tidb/util/testkit" "github.com/pingcap/tidb/util/testleak" ) @@ -424,6 +425,17 @@ func (ts *ConnTestSuite) TestConnExecutionTimeout(c *C) { err = cc.handleQuery(context.Background(), "select * FROM testTable2 WHERE SLEEP(1);") c.Assert(err, IsNil) + _, err = se.Execute(context.Background(), "set @@max_execution_time = 1500;") + c.Assert(err, IsNil) + + _, err = se.Execute(context.Background(), "set @@tidb_expensive_query_time_threshold = 1;") + c.Assert(err, IsNil) + + records, err := se.Execute(context.Background(), "select SLEEP(2);") + c.Assert(err, IsNil) + tk := testkit.NewTestKit(c, ts.store) + tk.ResultSetToResult(records[0], Commentf("%v", records[0])).Check(testkit.Rows("1")) + _, err = se.Execute(context.Background(), "set @@max_execution_time = 0;") c.Assert(err, IsNil) diff --git a/util/expensivequery/expensivequery.go b/util/expensivequery/expensivequery.go index aa02852289b8b..8ab115e900ce6 100644 --- a/util/expensivequery/expensivequery.go +++ b/util/expensivequery/expensivequery.go @@ -59,15 +59,16 @@ func (eqh *Handle) Run() { case <-ticker.C: processInfo := sm.ShowProcessList() for _, info := range processInfo { - if len(info.Info) == 0 || info.ExceedExpensiveTimeThresh { + if len(info.Info) == 0 { continue } costTime := time.Since(info.Time) - if costTime >= time.Second*time.Duration(threshold) && log.GetLevel() <= zapcore.WarnLevel { + if !info.ExceedExpensiveTimeThresh && costTime >= time.Second*time.Duration(threshold) && log.GetLevel() <= zapcore.WarnLevel { logExpensiveQuery(costTime, info) info.ExceedExpensiveTimeThresh = true + } - } else if info.MaxExecutionTime > 0 && costTime > time.Duration(info.MaxExecutionTime)*time.Millisecond { + if info.MaxExecutionTime > 0 && costTime > time.Duration(info.MaxExecutionTime)*time.Millisecond { sm.Kill(info.ID, true) } } From d3f1a7a6892a4303aa7b640e871c7178c629f7a6 Mon Sep 17 00:00:00 2001 From: Zejun Li Date: Mon, 1 Jun 2020 10:31:48 +0800 Subject: [PATCH 64/74] statistics: avoid large CMSketch affecting the latency of normal query (#17542) --- statistics/cmsketch.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/statistics/cmsketch.go b/statistics/cmsketch.go index ca2c09320fbd9..b731d38f93b55 100644 --- a/statistics/cmsketch.go +++ b/statistics/cmsketch.go @@ -60,8 +60,17 @@ func (t *TopNMeta) GetH2() uint64 { // NewCMSketch returns a new CM sketch. func NewCMSketch(d, w int32) *CMSketch { tbl := make([][]uint32, d) + // Background: The Go's memory allocator will ask caller to sweep spans in some scenarios. + // This can cause memory allocation request latency unpredictable, if the list of spans which need sweep is too long. + // For memory allocation large than 32K, the allocator will never allocate memory from spans list. + // + // The memory referenced by the CMSketch will never be freed. + // If the number of table or index is extremely large, there will be a large amount of spans in global list. + // The default value of `d` is 5 and `w` is 2048, if we use a single slice for them the size will be 40K. + // This allocation will be handled by mheap and will never have impact on normal allocations. + arena := make([]uint32, d*w) for i := range tbl { - tbl[i] = make([]uint32, w) + tbl[i] = arena[i*int(w) : (i+1)*int(w)] } return &CMSketch{depth: d, width: w, table: tbl} } From 9162cfa2f3b4d1fb1291f68c05dd6ddbbac4c9b7 Mon Sep 17 00:00:00 2001 From: Arenatlx Date: Mon, 1 Jun 2020 10:47:17 +0800 Subject: [PATCH 65/74] meta: fix the allocator batch size compute logic (#17271) --- meta/autoid/autoid.go | 49 ++++++++++++++++++++++++------------ meta/autoid/autoid_test.go | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 16 deletions(-) diff --git a/meta/autoid/autoid.go b/meta/autoid/autoid.go index 81359c061f945..db7d07ae4565f 100755 --- a/meta/autoid/autoid.go +++ b/meta/autoid/autoid.go @@ -373,6 +373,11 @@ func (alloc *allocator) GetType() AllocatorType { // NextStep return new auto id step according to previous step and consuming time. func NextStep(curStep int64, consumeDur time.Duration) int64 { + failpoint.Inject("mockAutoIDCustomize", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(3) + } + }) failpoint.Inject("mockAutoIDChange", func(val failpoint.Value) { if val.(bool) { failpoint.Return(step) @@ -640,14 +645,6 @@ func (alloc *allocator) alloc4Signed(tableID int64, n uint64, increment, offset consumeDur := startTime.Sub(alloc.lastAllocTime) nextStep = NextStep(alloc.step, consumeDur) } - // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. - if nextStep <= n1 { - nextStep = mathutil.MinInt64(n1*2, maxStep) - } - // Store the step for non-customized-step allocator to calculate next dynamic step. - if !alloc.customStep { - alloc.step = nextStep - } err := kv.RunInNewTxn(alloc.store, true, func(txn kv.Transaction) error { m := meta.NewMeta(txn) var err1 error @@ -655,6 +652,12 @@ func (alloc *allocator) alloc4Signed(tableID int64, n uint64, increment, offset if err1 != nil { return err1 } + // CalcNeededBatchSize calculates the total batch size needed on global base. + n1 = CalcNeededBatchSize(newBase, int64(n), increment, offset, alloc.isUnsigned) + // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. + if nextStep < n1 { + nextStep = n1 + } tmpStep := mathutil.MinInt64(math.MaxInt64-newBase, nextStep) // The global rest is not enough for alloc. if tmpStep < n1 { @@ -667,6 +670,10 @@ func (alloc *allocator) alloc4Signed(tableID int64, n uint64, increment, offset if err != nil { return 0, 0, err } + // Store the step for non-customized-step allocator to calculate next dynamic step. + if !alloc.customStep { + alloc.step = nextStep + } alloc.lastAllocTime = time.Now() if newBase == math.MaxInt64 { return 0, 0, ErrAutoincReadFailed @@ -707,14 +714,6 @@ func (alloc *allocator) alloc4Unsigned(tableID int64, n uint64, increment, offse consumeDur := startTime.Sub(alloc.lastAllocTime) nextStep = NextStep(alloc.step, consumeDur) } - // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. - if nextStep <= n1 { - nextStep = mathutil.MinInt64(n1*2, maxStep) - } - // Store the step for non-customized-step allocator to calculate next dynamic step. - if !alloc.customStep { - alloc.step = nextStep - } err := kv.RunInNewTxn(alloc.store, true, func(txn kv.Transaction) error { m := meta.NewMeta(txn) var err1 error @@ -722,6 +721,12 @@ func (alloc *allocator) alloc4Unsigned(tableID int64, n uint64, increment, offse if err1 != nil { return err1 } + // CalcNeededBatchSize calculates the total batch size needed on new base. + n1 = CalcNeededBatchSize(newBase, int64(n), increment, offset, alloc.isUnsigned) + // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. + if nextStep < n1 { + nextStep = n1 + } tmpStep := int64(mathutil.MinUint64(math.MaxUint64-uint64(newBase), uint64(nextStep))) // The global rest is not enough for alloc. if tmpStep < n1 { @@ -734,6 +739,10 @@ func (alloc *allocator) alloc4Unsigned(tableID int64, n uint64, increment, offse if err != nil { return 0, 0, err } + // Store the step for non-customized-step allocator to calculate next dynamic step. + if !alloc.customStep { + alloc.step = nextStep + } alloc.lastAllocTime = time.Now() if uint64(newBase) == math.MaxUint64 { return 0, 0, ErrAutoincReadFailed @@ -896,6 +905,14 @@ func DecodeCmpUintToInt(u uint64) int64 { return int64(u ^ signMask) } +// TestModifyBaseAndEndInjection exported for testing modifying the base and end. +func TestModifyBaseAndEndInjection(alloc Allocator, base, end int64) { + alloc.(*allocator).mu.Lock() + alloc.(*allocator).base = base + alloc.(*allocator).end = end + alloc.(*allocator).mu.Unlock() +} + // AutoRandomIDLayout is used to calculate the bits length of different section in auto_random id. // The primary key with auto_random can only be `bigint` column, the total layout length of auto random is 64 bits. // These are two type of layout: diff --git a/meta/autoid/autoid_test.go b/meta/autoid/autoid_test.go index 91c93f574a2ec..c39bee5e3e31a 100644 --- a/meta/autoid/autoid_test.go +++ b/meta/autoid/autoid_test.go @@ -825,3 +825,54 @@ func (*testSuite) TestConcurrentAllocSequence(c *C) { err = <-errCh c.Assert(err, IsNil) } + +// Fix a computation logic bug in allocator computation. +func (*testSuite) TestAllocComputationIssue(c *C) { + c.Assert(failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDCustomize", `return(true)`), IsNil) + defer func() { + c.Assert(failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDCustomize"), IsNil) + }() + + store, err := mockstore.NewMockStore() + c.Assert(err, IsNil) + defer store.Close() + + err = kv.RunInNewTxn(store, false, func(txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) + c.Assert(err, IsNil) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) + c.Assert(err, IsNil) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 2, Name: model.NewCIStr("t1")}) + c.Assert(err, IsNil) + return nil + }) + c.Assert(err, IsNil) + + // Since the test here is applicable to any type of allocators, autoid.RowIDAllocType is chosen. + unsignedAlloc := autoid.NewAllocator(store, 1, true, autoid.RowIDAllocType) + c.Assert(unsignedAlloc, NotNil) + signedAlloc := autoid.NewAllocator(store, 1, false, autoid.RowIDAllocType) + c.Assert(signedAlloc, NotNil) + + // the next valid two value must be 13 & 16, batch size = 6. + err = unsignedAlloc.Rebase(1, 10, false) + c.Assert(err, IsNil) + // the next valid two value must be 10 & 13, batch size = 6. + err = signedAlloc.Rebase(2, 7, false) + c.Assert(err, IsNil) + // Simulate the rest cache is not enough for next batch, assuming 10 & 13, batch size = 4. + autoid.TestModifyBaseAndEndInjection(unsignedAlloc, 9, 9) + // Simulate the rest cache is not enough for next batch, assuming 10 & 13, batch size = 4. + autoid.TestModifyBaseAndEndInjection(signedAlloc, 4, 6) + + // Here will recompute the new allocator batch size base on new base = 10, which will get 6. + min, max, err := unsignedAlloc.Alloc(1, 2, 3, 1) + c.Assert(err, IsNil) + c.Assert(min, Equals, int64(10)) + c.Assert(max, Equals, int64(16)) + min, max, err = signedAlloc.Alloc(2, 2, 3, 1) + c.Assert(err, IsNil) + c.Assert(min, Equals, int64(7)) + c.Assert(max, Equals, int64(13)) +} From 0f6b6a2013d9d165589a3a31bb914288854f83b5 Mon Sep 17 00:00:00 2001 From: Neil Shen Date: Mon, 1 Jun 2020 11:32:39 +0800 Subject: [PATCH 66/74] docs: add TiDB tools compatibility check (#17440) Signed-off-by: Neil Shen --- docs/design/TEMPLATE.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/design/TEMPLATE.md b/docs/design/TEMPLATE.md index bbe4178bc0f8e..df3656bced0a6 100644 --- a/docs/design/TEMPLATE.md +++ b/docs/design/TEMPLATE.md @@ -53,6 +53,13 @@ A discussion of alternate approaches and the trade-offs, advantages, and disadva