diff --git a/.gitignore b/.gitignore index 8649177261..7b7b55e30b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ /interpreter/test /tidb-server/tidb-server coverage.out +.idea/ +*.iml diff --git a/ast/misc.go b/ast/misc.go index 4f287b4680..8448d502d3 100644 --- a/ast/misc.go +++ b/ast/misc.go @@ -162,6 +162,7 @@ const ( ShowWarnings ShowCharset ShowVariables + ShowStatus ShowCollation ShowCreateTable ShowGrants diff --git a/ddl/ddl.go b/ddl/ddl.go index 361047fe17..2d18b9d338 100644 --- a/ddl/ddl.go +++ b/ddl/ddl.go @@ -33,7 +33,6 @@ import ( "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/parser/coldef" - "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/terror" "github.com/pingcap/tidb/util/charset" @@ -561,15 +560,6 @@ func (d *ddl) DropTable(ctx context.Context, ti table.Ident) (err error) { if err != nil { return errors.Trace(ErrNotExists) } - // Check Privilege - privChecker := privilege.GetPrivilegeChecker(ctx) - hasPriv, err := privChecker.Check(ctx, schema, tb.Meta(), mysql.DropPriv) - if err != nil { - return errors.Trace(err) - } - if !hasPriv { - return errors.Errorf("You do not have the privilege to drop table %s.%s.", ti.Schema, ti.Name) - } job := &model.Job{ SchemaID: schema.ID, diff --git a/expression/variable.go b/expression/variable.go index e58169fbde..9d1630cb9c 100644 --- a/expression/variable.go +++ b/expression/variable.go @@ -62,7 +62,7 @@ func (v *Variable) String() string { func (v *Variable) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) { name := strings.ToLower(v.Name) sessionVars := variable.GetSessionVars(ctx) - globalVars := variable.GetGlobalSysVarAccessor(ctx) + globalVars := variable.GetGlobalVarAccessor(ctx) if !v.IsSystem { // user vars if value, ok := sessionVars.Users[name]; ok { diff --git a/expression/variable_test.go b/expression/variable_test.go index eb6954fd8e..9c0e7091a3 100644 --- a/expression/variable_test.go +++ b/expression/variable_test.go @@ -30,7 +30,7 @@ func (s *testVariableSuite) SetUpSuite(c *C) { nc := mock.NewContext() s.ctx = nc variable.BindSessionVars(s.ctx) - variable.BindGlobalSysVarAccessor(s.ctx, nc) + variable.BindGlobalVarAccessor(s.ctx, nc) } func (s *testVariableSuite) TestVariable(c *C) { diff --git a/optimizer/convert_stmt.go b/optimizer/convert_stmt.go index bcd3a642e4..c4187f17ed 100644 --- a/optimizer/convert_stmt.go +++ b/optimizer/convert_stmt.go @@ -868,6 +868,8 @@ func convertShow(converter *expressionConverter, v *ast.ShowStmt) (*stmts.ShowSt oldShow.Target = stmt.ShowEngines case ast.ShowVariables: oldShow.Target = stmt.ShowVariables + case ast.ShowStatus: + oldShow.Target = stmt.ShowStatus case ast.ShowWarnings: oldShow.Target = stmt.ShowWarnings case ast.ShowNone: diff --git a/parser/parser.y b/parser/parser.y index 29dfe796e9..f0abb39d1d 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -228,6 +228,7 @@ import ( signed "SIGNED" some "SOME" start "START" + status "STATUS" stringType "string" subDate "SUBDATE" substring "SUBSTRING" @@ -482,10 +483,10 @@ import ( SelectStmtOpts "Select statement options" SelectStmtGroup "SELECT statement optional GROUP BY clause" SetStmt "Set variable statement" - ShowStmt "Show engines/databases/tables/columns/warnings statement" + ShowStmt "Show engines/databases/tables/columns/warnings/status statement" ShowDatabaseNameOpt "Show tables/columns statement database name option" ShowTableAliasOpt "Show table alias option" - ShowLikeOrWhereOpt "Show like or where condition option" + ShowLikeOrWhereOpt "Show like or where clause option" SignedLiteral "Literal or NumLiteral with sign" Statement "statement" StatementList "statement list" @@ -1647,7 +1648,7 @@ UnReservedKeyword: "AUTO_INCREMENT" | "AFTER" | "AVG" | "BEGIN" | "BIT" | "BOOL" | "BOOLEAN" | "CHARSET" | "COLUMNS" | "COMMIT" | "DATE" | "DATETIME" | "DEALLOCATE" | "DO" | "END" | "ENGINE" | "ENGINES" | "EXECUTE" | "FIRST" | "FULL" | "LOCAL" | "NAMES" | "OFFSET" | "PASSWORD" %prec lowerThanEq | "PREPARE" | "QUICK" | "ROLLBACK" | "SESSION" | "SIGNED" -| "START" | "GLOBAL" | "TABLES"| "TEXT" | "TIME" | "TIMESTAMP" | "TRANSACTION" | "TRUNCATE" | "UNKNOWN" +| "START" | "STATUS" | "GLOBAL" | "TABLES"| "TEXT" | "TIME" | "TIMESTAMP" | "TRANSACTION" | "TRUNCATE" | "UNKNOWN" | "VALUE" | "WARNINGS" | "YEAR" | "MODE" | "WEEK" | "ANY" | "SOME" | "USER" | "IDENTIFIED" | "COLLATION" | "COMMENT" | "AVG_ROW_LENGTH" | "CONNECTION" | "CHECKSUM" | "COMPRESSION" | "KEY_BLOCK_SIZE" | "MAX_ROWS" | "MIN_ROWS" | "NATIONAL" | "ROW" | "QUARTER" | "ESCAPE" | "GRANTS" @@ -3316,6 +3317,19 @@ ShowStmt: } $$ = stmt } +| "SHOW" GlobalScope "STATUS" ShowLikeOrWhereOpt + { + stmt := &ast.ShowStmt{ + Tp: ast.ShowStatus, + GlobalScope: $2.(bool), + } + if x, ok := $4.(*ast.PatternLikeExpr); ok { + stmt.Pattern = x + } else if $4 != nil { + stmt.Where = $4.(ast.ExprNode) + } + $$ = stmt + } | "SHOW" "COLLATION" ShowLikeOrWhereOpt { stmt := &ast.ShowStmt{ diff --git a/parser/parser_test.go b/parser/parser_test.go index 09a21e4a88..64f53c67ff 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -240,6 +240,11 @@ func (s *testParserSuite) TestDMLStmt(c *C) { {"SHOW VARIABLES", true}, {"SHOW GLOBAL VARIABLES", true}, {"SHOW GLOBAL VARIABLES WHERE Variable_name = 'autocommit'", true}, + {"SHOW STATUS", true}, + {"SHOW GLOBAL STATUS", true}, + {"SHOW SESSION STATUS", true}, + {"SHOW STATUS LIKE 'Up%'", true}, + {"SHOW STATUS WHERE Variable_name LIKE 'Up%'", true}, {`SHOW FULL TABLES FROM icar_qa LIKE play_evolutions`, true}, {`SHOW FULL TABLES WHERE Table_Type != 'VIEW'`, true}, {`SHOW GRANTS`, true}, diff --git a/parser/scanner.l b/parser/scanner.l index d13b209ac4..1eb7e4a793 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -430,6 +430,7 @@ share {s}{h}{a}{r}{e} show {s}{h}{o}{w} some {s}{o}{m}{e} start {s}{t}{a}{r}{t} +status {s}{t}{a}{t}{u}{s} subdate {s}{u}{b}{d}{a}{t}{e} substring {s}{u}{b}{s}{t}{r}{i}{n}{g} substring_index {s}{u}{b}{s}{t}{r}{i}{n}{g}_{i}{n}{d}{e}{x} @@ -841,6 +842,8 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h} return some {start} lval.item = string(l.val) return start +{status} lval.item = string(l.val) + return status {global} lval.item = string(l.val) return global {rand} lval.item = string(l.val) diff --git a/plan/plans/from.go b/plan/plans/from.go index bc744b5176..8844776c99 100644 --- a/plan/plans/from.go +++ b/plan/plans/from.go @@ -148,6 +148,7 @@ func (r *TableDefaultPlan) filterBinOp(ctx context.Context, x *expression.Binary return &indexPlan{ src: t, col: c, + unique: ix.Unique, idxName: ix.Name.O, idx: ix.X, spans: toSpans(x.Op, rval, seekVal), @@ -179,6 +180,7 @@ func (r *TableDefaultPlan) filterIdent(ctx context.Context, x *expression.Ident, return &indexPlan{ src: t, col: v, + unique: ix.Unique, idxName: ix.Name.L, idx: ix.X, spans: spans, @@ -215,6 +217,7 @@ func (r *TableDefaultPlan) filterIsNull(ctx context.Context, x *expression.IsNul return &indexPlan{ src: t, col: col, + unique: ix.Unique, idxName: ix.Name.L, idx: ix.X, spans: spans, diff --git a/plan/plans/index.go b/plan/plans/index.go index fcba257e7f..24ffa654cf 100644 --- a/plan/plans/index.go +++ b/plan/plans/index.go @@ -25,6 +25,7 @@ import ( "github.com/pingcap/tidb/parser/opcode" "github.com/pingcap/tidb/plan" "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/terror" "github.com/pingcap/tidb/util/format" "github.com/pingcap/tidb/util/types" ) @@ -124,6 +125,7 @@ func (span *indexSpan) cutOffHigh(val interface{}, exclude bool) *indexSpan { type indexPlan struct { src table.Table col *column.Col + unique bool idxName string idx kv.Index spans []*indexSpan // multiple spans are ordered by their values and without overlapping. @@ -243,6 +245,7 @@ func (r *indexPlan) Filter(ctx context.Context, expr expression.Expression) (pla return &indexPlan{ src: r.src, col: r.col, + unique: r.unique, idxName: r.idxName, idx: r.idx, spans: spans, @@ -325,19 +328,31 @@ func toSpans(op opcode.Op, val, seekVal interface{}) []*indexSpan { } // Next implements plan.Plan Next interface. -func (r *indexPlan) Next(ctx context.Context) (row *plan.Row, err error) { +func (r *indexPlan) Next(ctx context.Context) (*plan.Row, error) { for { if r.cursor == len(r.spans) { - return + return nil, nil } span := r.spans[r.cursor] + if r.isPointLookup(span) { + // Do point lookup on index will prevent prefetch cost. + row, err := r.pointLookup(ctx, span.lowVal) + if err != nil { + return nil, errors.Trace(err) + } + r.cursor++ + if row != nil { + return row, nil + } + continue + } if r.iter == nil { seekVal := span.seekVal if span.lowVal == minNotNullVal { seekVal = []byte{} } var txn kv.Transaction - txn, err = ctx.GetTxn(false) + txn, err := ctx.GetTxn(false) if err != nil { return nil, errors.Trace(err) } @@ -346,9 +361,7 @@ func (r *indexPlan) Next(ctx context.Context) (row *plan.Row, err error) { return nil, types.EOFAsNil(err) } } - var idxKey []interface{} - var h int64 - idxKey, h, err = r.iter.Next() + idxKey, h, err := r.iter.Next() if err != nil { return nil, types.EOFAsNil(err) } @@ -370,20 +383,58 @@ func (r *indexPlan) Next(ctx context.Context) (row *plan.Row, err error) { r.skipLowCmp = false continue } - row = &plan.Row{} - row.Data, err = r.src.Row(ctx, h) + var row *plan.Row + row, err = r.lookupRow(ctx, h) if err != nil { return nil, errors.Trace(err) } - rowKey := &plan.RowKeyEntry{ - Tbl: r.src, - Key: string(r.src.RecordKey(h, nil)), - } - row.RowKeys = append(row.RowKeys, rowKey) - return + return row, nil } } +func (r *indexPlan) isPointLookup(span *indexSpan) bool { + return r.unique && span.lowVal != nil && span.lowVal == span.highVal && !span.lowExclude && !span.highExclude +} + +func (r *indexPlan) lookupRow(ctx context.Context, h int64) (*plan.Row, error) { + row := &plan.Row{} + var err error + row.Data, err = r.src.Row(ctx, h) + if err != nil { + return nil, errors.Trace(err) + } + rowKey := &plan.RowKeyEntry{ + Tbl: r.src, + Key: string(r.src.RecordKey(h, nil)), + } + row.RowKeys = append(row.RowKeys, rowKey) + return row, nil +} + +// pointLookup do not seek index but call Exists method to get a handle, which is cheaper. +func (r *indexPlan) pointLookup(ctx context.Context, val interface{}) (*plan.Row, error) { + txn, err := ctx.GetTxn(false) + if err != nil { + return nil, errors.Trace(err) + } + var exist bool + var h int64 + // We expect a kv.ErrKeyExists Error because we pass -1 as the handle which is not equal to the existed handle. + exist, h, err = r.idx.Exist(txn, []interface{}{val}, -1) + if !exist { + return nil, errors.Trace(err) + } + if terror.ErrorNotEqual(kv.ErrKeyExists, err) { + return nil, errors.Trace(err) + } + var row *plan.Row + row, err = r.lookupRow(ctx, h) + if err != nil { + return nil, errors.Trace(err) + } + return row, nil +} + // Close implements plan.Plan Close interface. func (r *indexPlan) Close() error { if r.iter != nil { diff --git a/plan/plans/show.go b/plan/plans/show.go index e3590cc6d0..bb07f1f55a 100644 --- a/plan/plans/show.go +++ b/plan/plans/show.go @@ -32,6 +32,7 @@ import ( "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/stmt" "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/terror" "github.com/pingcap/tidb/util/charset" "github.com/pingcap/tidb/util/format" ) @@ -103,6 +104,8 @@ func (s *ShowPlan) GetFields() []*field.ResultField { types = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong} case stmt.ShowVariables: names = []string{"Variable_name", "Value"} + case stmt.ShowStatus: + names = []string{"Variable_name", "Value"} case stmt.ShowCollation: names = []string{"Collation", "Charset", "Id", "Default", "Compiled", "Sortlen"} types = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong, @@ -164,6 +167,8 @@ func (s *ShowPlan) fetchAll(ctx context.Context) error { return s.fetchShowCharset(ctx) case stmt.ShowVariables: return s.fetchShowVariables(ctx) + case stmt.ShowStatus: + return s.fetchShowStatus(ctx) case stmt.ShowCollation: return s.fetchShowCollation(ctx) case stmt.ShowCreateTable: @@ -353,7 +358,7 @@ func (s *ShowPlan) fetchShowTables(ctx context.Context) error { func (s *ShowPlan) fetchShowVariables(ctx context.Context) error { sessionVars := variable.GetSessionVars(ctx) - globalVars := variable.GetGlobalSysVarAccessor(ctx) + globalVars := variable.GetGlobalVarAccessor(ctx) m := map[interface{}]interface{}{} for _, v := range variable.SysVars { @@ -402,6 +407,86 @@ func (s *ShowPlan) fetchShowVariables(ctx context.Context) error { return nil } +func getSessionStatusVar(ctx context.Context, sessionVars *variable.SessionVars, + globalVars variable.GlobalVarAccessor, name string) (string, error) { + sv, ok := sessionVars.StatusVars[name] + if ok { + return sv, nil + } + + value, err := globalVars.GetGlobalStatusVar(ctx, name) + if err != nil && terror.UnknownStatusVar.Equal(err) { + return "", errors.Trace(err) + } + + return value, nil +} + +func getGlobalStatusVar(ctx context.Context, sessionVars *variable.SessionVars, + globalVars variable.GlobalVarAccessor, name string) (string, error) { + + value, err := globalVars.GetGlobalStatusVar(ctx, name) + if err == nil { + return value, nil + } + + if terror.UnknownStatusVar.Equal(err) { + return "", errors.Trace(err) + } + + sv, _ := sessionVars.StatusVars[name] + + return sv, nil +} + +func (s *ShowPlan) fetchShowStatus(ctx context.Context) error { + sessionVars := variable.GetSessionVars(ctx) + globalVars := variable.GetGlobalVarAccessor(ctx) + m := map[interface{}]interface{}{} + + for _, v := range variable.StatusVars { + if s.Pattern != nil { + s.Pattern.Expr = expression.Value{Val: v.Name} + } else if s.Where != nil { + m[expression.ExprEvalIdentFunc] = func(name string) (interface{}, error) { + if strings.EqualFold(name, "Variable_name") { + return v.Name, nil + } + + return nil, errors.Errorf("unknown field %s", name) + } + } + + match, err := s.evalCondition(ctx, m) + if err != nil { + return errors.Trace(err) + } + if !match { + continue + } + + var value string + if !s.GlobalScope { + value, err = getSessionStatusVar(ctx, sessionVars, globalVars, v.Name) + if err != nil { + return errors.Trace(err) + } + } else if v.Scope != variable.ScopeSession { + value, err = getGlobalStatusVar(ctx, sessionVars, globalVars, v.Name) + if err != nil { + return errors.Trace(err) + } + } else { + continue + } + + row := &plan.Row{Data: []interface{}{v.Name, value}} + s.rows = append(s.rows, row) + } + + return nil +} + func (s *ShowPlan) fetchShowCharset(ctx context.Context) error { // See: http://dev.mysql.com/doc/refman/5.7/en/show-character-set.html descs := charset.GetAllCharsets() diff --git a/plan/plans/show_test.go b/plan/plans/show_test.go index 54e1462eb0..17f0f8a949 100644 --- a/plan/plans/show_test.go +++ b/plan/plans/show_test.go @@ -56,7 +56,7 @@ func (p *testShowSuit) SetUpSuite(c *C) { nc := mock.NewContext() p.ctx = nc variable.BindSessionVars(p.ctx) - variable.BindGlobalSysVarAccessor(p.ctx, nc) + variable.BindGlobalVarAccessor(p.ctx, nc) p.dbName = "testshowplan" p.store = newStore(c, p.dbName) @@ -106,7 +106,7 @@ func (p *testShowSuit) TestSimple(c *C) { c.Assert(fls, HasLen, 2) } -func (p *testShowSuit) TestShowVariables(c *C) { +func (p *testShowSuit) TestShowSysVariables(c *C) { pln := &plans.ShowPlan{ Target: stmt.ShowVariables, GlobalScope: true, @@ -182,6 +182,94 @@ func (p *testShowSuit) TestShowVariables(c *C) { c.Assert(v, Equals, "on") } +func (p *testShowSuit) TestShowStatusVariables(c *C) { + pln := &plans.ShowPlan{ + Target: stmt.ShowStatus, + GlobalScope: true, + Pattern: &expression.PatternLike{ + Pattern: &expression.Value{ + Val: "tc_log_page_size", + }, + }, + } + fls := pln.GetFields() + c.Assert(fls, HasLen, 2) + c.Assert(fls[0].Name, Equals, "Variable_name") + c.Assert(fls[1].Name, Equals, "Value") + c.Assert(fls[0].Col.Tp, Equals, mysql.TypeVarchar) + c.Assert(fls[1].Col.Tp, Equals, mysql.TypeVarchar) + + sessionVars := variable.GetSessionVars(p.ctx) + ret := map[string]string{} + rset := rsets.Recordset{ + Ctx: p.ctx, + Plan: pln, + } + rset.Do(func(data []interface{}) (bool, error) { + ret[data[0].(string)] = data[1].(string) + return true, nil + }) + c.Assert(ret, HasLen, 1) + v, ok := ret["tc_log_page_size"] + c.Assert(ok, IsTrue) + c.Assert(v, Equals, "0") + pln.Close() + + sessionVars.StatusVars["tc_log_page_size"] = "1024" + pln.GlobalScope = false + rset.Do(func(data []interface{}) (bool, error) { + ret[data[0].(string)] = data[1].(string) + return true, nil + }) + c.Assert(ret, HasLen, 1) + v, ok = ret["tc_log_page_size"] + c.Assert(ok, IsTrue) + c.Assert(v, Equals, "1024") + pln.Close() + + pln.Pattern = &expression.PatternLike{ + Pattern: &expression.Value{ + Val: "compression", + }, + } + sessionVars.StatusVars["compression"] = "on" + pln.GlobalScope = true + ret = map[string]string{} + rset.Do(func(data []interface{}) (bool, error) { + ret[data[0].(string)] = data[1].(string) + return true, nil + }) + c.Assert(ret, HasLen, 0) + + pln.GlobalScope = false + rset.Do(func(data []interface{}) (bool, error) { + ret[data[0].(string)] = data[1].(string) + return true, nil + }) + c.Assert(ret, HasLen, 1) + v, ok = ret["compression"] + c.Assert(ok, IsTrue) + c.Assert(v, Equals, "on") + pln.Close() + + pln.Pattern = nil + pln.Where = &expression.BinaryOperation{ + L: &expression.Ident{CIStr: model.NewCIStr("Variable_name")}, + R: expression.Value{Val: "aborted_clients"}, + Op: opcode.EQ, + } + ret = map[string]string{} + sessionVars.StatusVars["aborted_clients"] = "0" + rset.Do(func(data []interface{}) (bool, error) { + ret[data[0].(string)] = data[1].(string) + return true, nil + }) + c.Assert(ret, HasLen, 1) + v, ok = ret["aborted_clients"] + c.Assert(ok, IsTrue) + c.Assert(v, Equals, "0") +} + func (p *testShowSuit) TestIssue540(c *C) { // Show variables where variable_name="time_zone" pln := &plans.ShowPlan{ diff --git a/session.go b/session.go index 2abfec1f96..686d19fc21 100644 --- a/session.go +++ b/session.go @@ -265,9 +265,9 @@ func (s *session) ExecRestrictedSQL(ctx context.Context, sql string) (rset.Recor return rs, errors.Trace(err) } -// GetGlobalSysVar implements RestrictedSQLExecutor.GetGlobalSysVar interface. -func (s *session) GetGlobalSysVar(ctx context.Context, name string) (string, error) { - sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s";`, mysql.SystemDB, mysql.GlobalVariablesTable, name) +// getExecRet executes restricted sql and the result is one column. +// It returns a string value. +func (s *session) getExecRet(ctx context.Context, sql string) (string, error) { rs, err := s.ExecRestrictedSQL(ctx, sql) if err != nil { return "", errors.Trace(err) @@ -278,7 +278,7 @@ func (s *session) GetGlobalSysVar(ctx context.Context, name string) (string, err return "", errors.Trace(err) } if row == nil { - return "", fmt.Errorf("Unknown sys var: %s", name) + return "", terror.ExecResultIsEmpty } value, err := types.ToString(row.Data[0]) if err != nil { @@ -287,7 +287,44 @@ func (s *session) GetGlobalSysVar(ctx context.Context, name string) (string, err return value, nil } -// SetGlobalSysVar implements RestrictedSQLExecutor.SetGlobalSysVar interface. +// GetGlobalStatusVar implements GlobalVarAccessor.GetGlobalStatusVar interface. +func (s *session) GetGlobalStatusVar(ctx context.Context, name string) (string, error) { + // TODO: get global status variables from store. + v := variable.GetStatusVar(name) + if v == nil { + return "", terror.UnknownStatusVar.Gen("unknown status variable:%s", name) + } + + return v.Value, nil +} + +// SetGlobalStatusVar implements GlobalVarAccessor.SetGlobalStatusVar interface. +func (s *session) SetGlobalStatusVar(ctx context.Context, name string, value string) error { + // TODO: set global status variables from store. + v := variable.GetStatusVar(name) + if v == nil { + return terror.UnknownStatusVar.Gen("unknown status variable:%s", name) + } + v.Value = value + + return nil +} + +// GetGlobalSysVar implements GlobalVarAccessor.GetGlobalSysVar interface. +func (s *session) GetGlobalSysVar(ctx context.Context, name string) (string, error) { + sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s";`, mysql.SystemDB, mysql.GlobalVariablesTable, name) + sysVar, err := s.getExecRet(ctx, sql) + if err != nil { + if terror.ExecResultIsEmpty.Equal(err) { + return "", terror.ExecResultIsEmpty.Gen("unknown sys variable:%s", name) + } + return "", errors.Trace(err) + } + + return sysVar, nil +} + +// SetGlobalSysVar implements GlobalVarAccessor.SetGlobalSysVar interface. func (s *session) SetGlobalSysVar(ctx context.Context, name string, value string) error { sql := fmt.Sprintf(`UPDATE %s.%s SET VARIABLE_VALUE="%s" WHERE VARIABLE_NAME="%s";`, mysql.SystemDB, mysql.GlobalVariablesTable, value, strings.ToLower(name)) _, err := s.ExecRestrictedSQL(ctx, sql) @@ -545,8 +582,8 @@ func CreateSession(store kv.Storage) (Session, error) { variable.BindSessionVars(s) variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusAutocommit, true) - // session implements variable.GlobalSysVarAccessor. Bind it to ctx. - variable.BindGlobalSysVarAccessor(s, s) + // session implements variable.GlobalVarAccessor. Bind it to ctx. + variable.BindGlobalVarAccessor(s, s) // session implements autocommit.Checker. Bind it to ctx autocommit.BindAutocommitChecker(s, s) diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 23c04e0ebb..4e34da40ae 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -23,6 +23,8 @@ type SessionVars struct { Users map[string]string // system variables Systems map[string]string + // status variables + StatusVars map[string]string // prepared statement PreparedStmts map[string]interface{} // prepared statement auto increment id @@ -58,6 +60,7 @@ func BindSessionVars(ctx context.Context) { v := &SessionVars{ Users: make(map[string]string), Systems: make(map[string]string), + StatusVars: make(map[string]string), PreparedStmts: make(map[string]interface{}), } diff --git a/sessionctx/variable/statusvar.go b/sessionctx/variable/statusvar.go new file mode 100644 index 0000000000..1c6fe39103 --- /dev/null +++ b/sessionctx/variable/statusvar.go @@ -0,0 +1,391 @@ +// Copyright 2015 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 variable + +import ( + "strings" +) + +// StatusVars is global status vars map. +var StatusVars map[string]*SysVar + +// GetStatusVar returns status var infomation for name. +func GetStatusVar(name string) *SysVar { + name = strings.ToLower(name) + return StatusVars[name] +} + +func init() { + StatusVars = make(map[string]*SysVar) + for _, v := range defaultStatusVars { + StatusVars[v.Name] = v + } +} + +var defaultStatusVars = []*SysVar{ + {ScopeGlobal, "aborted_clients", "0"}, + {ScopeGlobal, "binlog_cache_disk_use", "0"}, + {ScopeGlobal, "binlog_cache_use", "0"}, + {ScopeGlobal, "binlog_stmt_cache_disk_use", "0"}, + {ScopeGlobal, "binlog_stmt_cache_use", "0"}, + {ScopeGlobal | ScopeSession, "bytes_received", "2262"}, + {ScopeGlobal | ScopeSession, "bytes_sent", "106142"}, + {ScopeGlobal | ScopeSession, "com_admin_commands", "0"}, + {ScopeGlobal | ScopeSession, "com_assign_to_keycache", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_db", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_db_upgrade", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_event", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_function", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_procedure", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_server", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_table", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_tablespace", "0"}, + {ScopeGlobal | ScopeSession, "com_alter_user", "0"}, + {ScopeGlobal | ScopeSession, "com_analyze", "0"}, + {ScopeGlobal | ScopeSession, "com_begin", "0"}, + {ScopeGlobal | ScopeSession, "com_binlog", "0"}, + {ScopeGlobal | ScopeSession, "com_call_procedure", "0"}, + {ScopeGlobal | ScopeSession, "com_change_db", "2"}, + {ScopeGlobal | ScopeSession, "com_change_master", "0"}, + {ScopeGlobal | ScopeSession, "com_change_repl_filter", "0"}, + {ScopeGlobal | ScopeSession, "com_check", "0"}, + {ScopeGlobal | ScopeSession, "com_checksum", "0"}, + {ScopeGlobal | ScopeSession, "com_commit", "0"}, + {ScopeGlobal | ScopeSession, "com_create_db", "1"}, + {ScopeGlobal | ScopeSession, "com_create_event", "0"}, + {ScopeGlobal | ScopeSession, "com_create_function", "0"}, + {ScopeGlobal | ScopeSession, "com_create_index", "0"}, + {ScopeGlobal | ScopeSession, "com_create_procedure", "0"}, + {ScopeGlobal | ScopeSession, "com_create_server", "0"}, + {ScopeGlobal | ScopeSession, "com_create_table", "1"}, + {ScopeGlobal | ScopeSession, "com_create_trigger", "0"}, + {ScopeGlobal | ScopeSession, "com_create_udf", "0"}, + {ScopeGlobal | ScopeSession, "com_create_user", "0"}, + {ScopeGlobal | ScopeSession, "com_create_view", "0"}, + {ScopeGlobal | ScopeSession, "com_dealloc_sql", "0"}, + {ScopeGlobal | ScopeSession, "com_delete", "0"}, + {ScopeGlobal | ScopeSession, "com_delete_multi", "0"}, + {ScopeGlobal | ScopeSession, "com_do", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_db", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_event", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_function", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_index", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_procedure", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_server", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_table", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_trigger", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_user", "0"}, + {ScopeGlobal | ScopeSession, "com_drop_view", "0"}, + {ScopeGlobal | ScopeSession, "com_empty_query", "0"}, + {ScopeGlobal | ScopeSession, "com_execute_sql", "0"}, + {ScopeGlobal | ScopeSession, "com_explain_other", "0"}, + {ScopeGlobal | ScopeSession, "com_flush", "0"}, + {ScopeGlobal | ScopeSession, "com_get_diagnostics", "0"}, + {ScopeGlobal | ScopeSession, "com_grant", "0"}, + {ScopeGlobal | ScopeSession, "com_ha_close", "0"}, + {ScopeGlobal | ScopeSession, "com_ha_open", "0"}, + {ScopeGlobal | ScopeSession, "com_ha_read", "0"}, + {ScopeGlobal | ScopeSession, "com_help", "0"}, + {ScopeGlobal | ScopeSession, "com_insert", "0"}, + {ScopeGlobal | ScopeSession, "com_insert_select", "0"}, + {ScopeGlobal | ScopeSession, "com_install_plugin", "0"}, + {ScopeGlobal | ScopeSession, "com_kill", "0"}, + {ScopeGlobal | ScopeSession, "com_load", "0"}, + {ScopeGlobal | ScopeSession, "com_lock_tables", "0"}, + {ScopeGlobal | ScopeSession, "com_optimize", "0"}, + {ScopeGlobal | ScopeSession, "com_preload_keys", "0"}, + {ScopeGlobal | ScopeSession, "com_prepare_sql", "0"}, + {ScopeGlobal | ScopeSession, "com_purge", "0"}, + {ScopeGlobal | ScopeSession, "com_purge_before_date", "0"}, + {ScopeGlobal | ScopeSession, "com_release_savepoint", "0"}, + {ScopeGlobal | ScopeSession, "com_rename_table", "0"}, + {ScopeGlobal | ScopeSession, "com_rename_user", "0"}, + {ScopeGlobal | ScopeSession, "com_repair", "0"}, + {ScopeGlobal | ScopeSession, "com_replace", "0"}, + {ScopeGlobal | ScopeSession, "com_replace_select", "0"}, + {ScopeGlobal | ScopeSession, "com_reset", "0"}, + {ScopeGlobal | ScopeSession, "com_resignal", "0"}, + {ScopeGlobal | ScopeSession, "com_revoke", "0"}, + {ScopeGlobal | ScopeSession, "com_revoke_all", "0"}, + {ScopeGlobal | ScopeSession, "com_rollback", "0"}, + {ScopeGlobal | ScopeSession, "com_rollback_to_savepoint", "0"}, + {ScopeGlobal | ScopeSession, "com_savepoint", "0"}, + {ScopeGlobal | ScopeSession, "com_select", "9"}, + {ScopeGlobal | ScopeSession, "com_set_option", "0"}, + {ScopeGlobal | ScopeSession, "com_signal", "0"}, + {ScopeGlobal | ScopeSession, "com_show_binlog_events", "0"}, + {ScopeGlobal | ScopeSession, "com_show_binlogs", "0"}, + {ScopeGlobal | ScopeSession, "com_show_charsets", "0"}, + {ScopeGlobal | ScopeSession, "com_show_collations", "0"}, + {ScopeGlobal | ScopeSession, "com_show_create_db", "0"}, + {ScopeGlobal | ScopeSession, "com_show_create_event", "0"}, + {ScopeGlobal | ScopeSession, "com_show_create_func", "0"}, + {ScopeGlobal | ScopeSession, "com_show_create_proc", "0"}, + {ScopeGlobal | ScopeSession, "com_show_create_table", "0"}, + {ScopeGlobal | ScopeSession, "com_show_create_trigger", "0"}, + {ScopeGlobal | ScopeSession, "com_show_databases", "1"}, + {ScopeGlobal | ScopeSession, "com_show_engine_logs", "0"}, + {ScopeGlobal | ScopeSession, "com_show_engine_mutex", "0"}, + {ScopeGlobal | ScopeSession, "com_show_engine_status", "0"}, + {ScopeGlobal | ScopeSession, "com_show_events", "0"}, + {ScopeGlobal | ScopeSession, "com_show_errors", "0"}, + {ScopeGlobal | ScopeSession, "com_show_fields", "0"}, + {ScopeGlobal | ScopeSession, "com_show_function_code", "0"}, + {ScopeGlobal | ScopeSession, "com_show_function_status", "0"}, + {ScopeGlobal | ScopeSession, "com_show_grants", "0"}, + {ScopeGlobal | ScopeSession, "com_show_keys", "0"}, + {ScopeGlobal | ScopeSession, "com_show_master_status", "0"}, + {ScopeGlobal | ScopeSession, "com_show_open_tables", "0"}, + {ScopeGlobal | ScopeSession, "com_show_plugins", "0"}, + {ScopeGlobal | ScopeSession, "com_show_privileges", "0"}, + {ScopeGlobal | ScopeSession, "com_show_procedure_code", "0"}, + {ScopeGlobal | ScopeSession, "com_show_procedure_status", "0"}, + {ScopeGlobal | ScopeSession, "com_show_processlist", "0"}, + {ScopeGlobal | ScopeSession, "com_show_profile", "0"}, + {ScopeGlobal | ScopeSession, "com_show_profiles", "0"}, + {ScopeGlobal | ScopeSession, "com_show_relaylog_events", "0"}, + {ScopeGlobal | ScopeSession, "com_show_slave_hosts", "0"}, + {ScopeGlobal | ScopeSession, "com_show_slave_status", "0"}, + {ScopeGlobal | ScopeSession, "com_show_status", "25"}, + {ScopeGlobal | ScopeSession, "com_show_storage_engines", "0"}, + {ScopeGlobal | ScopeSession, "com_show_table_status", "0"}, + {ScopeGlobal | ScopeSession, "com_show_tables", "2"}, + {ScopeGlobal | ScopeSession, "com_show_triggers", "0"}, + {ScopeGlobal | ScopeSession, "com_show_variables", "4"}, + {ScopeGlobal | ScopeSession, "com_show_warnings", "2"}, + {ScopeGlobal | ScopeSession, "com_show_create_user", "0"}, + {ScopeGlobal | ScopeSession, "com_shutdown", "0"}, + {ScopeGlobal | ScopeSession, "com_slave_start", "0"}, + {ScopeGlobal | ScopeSession, "com_slave_stop", "0"}, + {ScopeGlobal | ScopeSession, "com_group_replication_start", "0"}, + {ScopeGlobal | ScopeSession, "com_group_replication_stop", "0"}, + {ScopeGlobal | ScopeSession, "com_stmt_execute", "0"}, + {ScopeGlobal | ScopeSession, "com_stmt_close", "0"}, + {ScopeGlobal | ScopeSession, "com_stmt_fetch", "0"}, + {ScopeGlobal | ScopeSession, "com_stmt_prepare", "0"}, + {ScopeGlobal | ScopeSession, "com_stmt_reset", "0"}, + {ScopeGlobal | ScopeSession, "com_stmt_send_long_data", "0"}, + {ScopeGlobal | ScopeSession, "com_truncate", "0"}, + {ScopeGlobal | ScopeSession, "com_uninstall_plugin", "0"}, + {ScopeGlobal | ScopeSession, "com_unlock_tables", "0"}, + {ScopeGlobal | ScopeSession, "com_update", "0"}, + {ScopeGlobal | ScopeSession, "com_update_multi", "0"}, + {ScopeGlobal | ScopeSession, "com_xa_commit", "0"}, + {ScopeGlobal | ScopeSession, "com_xa_end", "0"}, + {ScopeGlobal | ScopeSession, "com_xa_prepare", "0"}, + {ScopeGlobal | ScopeSession, "com_xa_recover", "0"}, + {ScopeGlobal | ScopeSession, "com_xa_rollback", "0"}, + {ScopeGlobal | ScopeSession, "com_xa_start", "0"}, + {ScopeGlobal | ScopeSession, "com_stmt_reprepare", "0"}, + {ScopeSession, "compression", "off"}, + {ScopeGlobal, "connection_errors_accept", "0"}, + {ScopeGlobal, "connection_errors_internal", "0"}, + {ScopeGlobal, "connection_errors_max_connections", "0"}, + {ScopeGlobal, "connection_errors_peer_address", "0"}, + {ScopeGlobal, "connection_errors_select", "0"}, + {ScopeGlobal, "connection_errors_tcpwrap", "0"}, + {ScopeGlobal, "connections", "3"}, + {ScopeGlobal | ScopeSession, "created_tmp_disk_tables", "0"}, + {ScopeGlobal, "created_tmp_files", "6"}, + {ScopeGlobal | ScopeSession, "created_tmp_tables", "3"}, + {ScopeGlobal, "delayed_errors", "0"}, + {ScopeGlobal, "delayed_insert_threads", "0"}, + {ScopeGlobal, "delayed_writes", "0"}, + {ScopeGlobal, "flush_commands", "1"}, + {ScopeGlobal | ScopeSession, "handler_commit", "5"}, + {ScopeGlobal | ScopeSession, "handler_delete", "0"}, + {ScopeGlobal | ScopeSession, "handler_discover", "0"}, + {ScopeGlobal | ScopeSession, "handler_external_lock", "265"}, + {ScopeGlobal | ScopeSession, "handler_mrr_init", "0"}, + {ScopeGlobal | ScopeSession, "handler_prepare", "0"}, + {ScopeGlobal | ScopeSession, "handler_read_first", "8"}, + {ScopeGlobal | ScopeSession, "handler_read_key", "6"}, + {ScopeGlobal | ScopeSession, "handler_read_last", "0"}, + {ScopeGlobal | ScopeSession, "handler_read_next", "1"}, + {ScopeGlobal | ScopeSession, "handler_read_prev", "0"}, + {ScopeGlobal | ScopeSession, "handler_read_rnd", "0"}, + {ScopeGlobal | ScopeSession, "handler_read_rnd_next", "7642"}, + {ScopeGlobal | ScopeSession, "handler_rollback", "0"}, + {ScopeGlobal | ScopeSession, "handler_savepoint", "0"}, + {ScopeGlobal | ScopeSession, "handler_savepoint_rollback", "0"}, + {ScopeGlobal | ScopeSession, "handler_update", "0"}, + {ScopeGlobal | ScopeSession, "handler_write", "6"}, + {ScopeGlobal, "innodb_buffer_pool_dump_status", "notstarted"}, + {ScopeGlobal, "innodb_buffer_pool_load_status", "Bufferpool(s)loadcom"}, + {ScopeGlobal, "innodb_buffer_pool_resize_status", "notstarted"}, + {ScopeGlobal, "innodb_buffer_pool_pages_data", "440"}, + {ScopeGlobal, "innodb_buffer_pool_bytes_data", "7208960"}, + {ScopeGlobal, "innodb_buffer_pool_pages_dirty", "0"}, + {ScopeGlobal, "innodb_buffer_pool_bytes_dirty", "0"}, + {ScopeGlobal, "innodb_buffer_pool_pages_flushed", "54"}, + {ScopeGlobal, "innodb_buffer_pool_pages_free", "7751"}, + {ScopeGlobal, "innodb_buffer_pool_pages_misc", "0"}, + {ScopeGlobal, "innodb_buffer_pool_pages_total", "8191"}, + {ScopeGlobal, "innodb_buffer_pool_read_ahead_rnd", "0"}, + {ScopeGlobal, "innodb_buffer_pool_read_ahead", "0"}, + {ScopeGlobal, "innodb_buffer_pool_read_ahead_evicted", "0"}, + {ScopeGlobal, "innodb_buffer_pool_read_requests", "1466"}, + {ScopeGlobal, "innodb_buffer_pool_reads", "402"}, + {ScopeGlobal, "innodb_buffer_pool_wait_free", "0"}, + {ScopeGlobal, "innodb_buffer_pool_write_requests", "400"}, + {ScopeGlobal, "innodb_data_fsyncs", "24"}, + {ScopeGlobal, "innodb_data_pending_fsyncs", "0"}, + {ScopeGlobal, "innodb_data_pending_reads", "0"}, + {ScopeGlobal, "innodb_data_pending_writes", "0"}, + {ScopeGlobal, "innodb_data_read", "6656512"}, + {ScopeGlobal, "innodb_data_reads", "426"}, + {ScopeGlobal, "innodb_data_writes", "84"}, + {ScopeGlobal, "innodb_data_written", "1232384"}, + {ScopeGlobal, "innodb_dblwr_pages_written", "20"}, + {ScopeGlobal, "innodb_dblwr_writes", "2"}, + {ScopeGlobal, "innodb_log_waits", "0"}, + {ScopeGlobal, "innodb_log_write_requests", "14"}, + {ScopeGlobal, "innodb_log_writes", "8"}, + {ScopeGlobal, "innodb_os_log_fsyncs", "12"}, + {ScopeGlobal, "innodb_os_log_pending_fsyncs", "0"}, + {ScopeGlobal, "innodb_os_log_pending_writes", "0"}, + {ScopeGlobal, "innodb_os_log_written", "17920"}, + {ScopeGlobal, "innodb_page_size", "16384"}, + {ScopeGlobal, "innodb_pages_created", "39"}, + {ScopeGlobal, "innodb_pages_read", "401"}, + {ScopeGlobal, "innodb_pages_written", "54"}, + {ScopeGlobal, "innodb_row_lock_current_waits", "0"}, + {ScopeGlobal, "innodb_row_lock_time", "0"}, + {ScopeGlobal, "innodb_row_lock_time_avg", "0"}, + {ScopeGlobal, "innodb_row_lock_time_max", "0"}, + {ScopeGlobal, "innodb_row_lock_waits", "0"}, + {ScopeGlobal, "innodb_rows_deleted", "0"}, + {ScopeGlobal, "innodb_rows_inserted", "0"}, + {ScopeGlobal, "innodb_rows_read", "8"}, + {ScopeGlobal, "innodb_rows_updated", "0"}, + {ScopeGlobal, "innodb_num_open_files", "17"}, + {ScopeGlobal, "innodb_truncated_status_writes", "0"}, + {ScopeGlobal, "innodb_available_undo_logs", "128"}, + {ScopeSession, "last_query_cost", "104.799000"}, + {ScopeSession, "last_query_partial_plans", "1"}, + {ScopeGlobal, "key_blocks_not_flushed", "0"}, + {ScopeGlobal, "key_blocks_unused", "6695"}, + {ScopeGlobal, "key_blocks_used", "3"}, + {ScopeGlobal, "key_read_requests", "6"}, + {ScopeGlobal, "key_reads", "3"}, + {ScopeGlobal, "key_write_requests", "0"}, + {ScopeGlobal, "key_writes", "0"}, + {ScopeGlobal | ScopeSession, "locked_connects", "0"}, + {ScopeGlobal | ScopeSession, "max_execution_time_exceeded", "0"}, + {ScopeGlobal | ScopeSession, "max_execution_time_set", "0"}, + {ScopeGlobal | ScopeSession, "max_execution_time_set_failed", "0"}, + {ScopeGlobal, "max_used_connections", "1"}, + {ScopeGlobal, "max_used_connections_time", "2015-11-0902:49:42"}, + {ScopeGlobal, "not_flushed_delayed_rows", "0"}, + {ScopeGlobal, "ongoing_anonymous_transaction_count", "0"}, + {ScopeGlobal, "open_files", "14"}, + {ScopeGlobal, "open_streams", "0"}, + {ScopeGlobal, "open_table_definitions", "105"}, + {ScopeGlobal | ScopeSession, "Open_tables", "101"}, + {ScopeGlobal, "opened_files", "142"}, + {ScopeGlobal | ScopeSession, "opened_table_definitions", "106"}, + {ScopeGlobal | ScopeSession, "opened_tables", "108"}, + {ScopeGlobal, "performance_schema_accounts_lost", "0"}, + {ScopeGlobal, "performance_schema_cond_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_cond_instances_lost", "0"}, + {ScopeGlobal, "performance_schema_digest_lost", "0"}, + {ScopeGlobal, "performance_schema_file_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_file_handles_lost", "0"}, + {ScopeGlobal, "performance_schema_file_instances_lost", "0"}, + {ScopeGlobal, "performance_schema_hosts_lost", "0"}, + {ScopeGlobal, "performance_schema_index_stat_lost", "0"}, + {ScopeGlobal, "performance_schema_locker_lost", "0"}, + {ScopeGlobal, "performance_schema_memory_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_metadata_lock_lost", "0"}, + {ScopeGlobal, "performance_schema_mutex_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_mutex_instances_lost", "0"}, + {ScopeGlobal, "performance_schema_nested_statement_lost", "0"}, + {ScopeGlobal, "performance_schema_prepared_statements_lost", "0"}, + {ScopeGlobal, "performance_schema_program_lost", "0"}, + {ScopeGlobal, "performance_schema_rwlock_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_rwlock_instances_lost", "0"}, + {ScopeGlobal, "performance_schema_session_connect_attrs_lost", "0"}, + {ScopeGlobal, "performance_schema_socket_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_socket_instances_lost", "0"}, + {ScopeGlobal, "performance_schema_stage_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_statement_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_table_handles_lost", "0"}, + {ScopeGlobal, "performance_schema_table_instances_lost", "0"}, + {ScopeGlobal, "performance_schema_table_lock_stat_lost", "0"}, + {ScopeGlobal, "performance_schema_thread_classes_lost", "0"}, + {ScopeGlobal, "performance_schema_thread_instances_lost", "0"}, + {ScopeGlobal, "performance_schema_users_lost", "0"}, + {ScopeGlobal, "prepared_stmt_count", "0"}, + {ScopeGlobal, "qcache_free_blocks", "1"}, + {ScopeGlobal, "qcache_free_memory", "1031832"}, + {ScopeGlobal, "qcache_hits", "0"}, + {ScopeGlobal, "qcache_inserts", "0"}, + {ScopeGlobal, "qcache_lowmem_prunes", "0"}, + {ScopeGlobal, "qcache_not_cached", "5"}, + {ScopeGlobal, "qcache_queries_in_cache", "0"}, + {ScopeGlobal, "qcache_total_blocks", "1"}, + {ScopeGlobal | ScopeSession, "queries", "56"}, + {ScopeGlobal | ScopeSession, "questions", "54"}, + {ScopeGlobal | ScopeSession, "select_full_join", "0"}, + {ScopeGlobal | ScopeSession, "select_full_range_join", "0"}, + {ScopeGlobal | ScopeSession, "select_range", "0"}, + {ScopeGlobal | ScopeSession, "select_range_check", "0"}, + {ScopeGlobal | ScopeSession, "select_scan", "24"}, + {ScopeGlobal, "slave_open_temp_tables", "0"}, + {ScopeGlobal | ScopeSession, "slow_launch_threads", "0"}, + {ScopeGlobal | ScopeSession, "slow_queries", "0"}, + {ScopeGlobal | ScopeSession, "sort_merge_passes", "0"}, + {ScopeGlobal | ScopeSession, "sort_range", "0"}, + {ScopeGlobal | ScopeSession, "sort_rows", "0"}, + {ScopeGlobal | ScopeSession, "sort_scan", "0"}, + {ScopeGlobal, "ssl_accept_renegotiates", "0"}, + {ScopeGlobal, "ssl_accepts", "0"}, + {ScopeGlobal, "ssl_callback_cache_hits", "0"}, + {ScopeGlobal | ScopeSession, "ssl_cipher", ""}, + {ScopeGlobal | ScopeSession, "ssl_cipher_list", ""}, + {ScopeGlobal, "ssl_client_connects", "0"}, + {ScopeGlobal, "ssl_connect_renegotiates", "0"}, + {ScopeGlobal, "ssl_ctx_verify_depth", "0"}, + {ScopeGlobal, "ssl_ctx_verify_mode", "0"}, + {ScopeGlobal | ScopeSession, "Ssl_default_timeout", "0"}, + {ScopeGlobal, "ssl_finished_accepts", "0"}, + {ScopeGlobal, "ssl_finished_connects", "0"}, + {ScopeGlobal | ScopeSession, "ssl_server_not_after", ""}, + {ScopeGlobal | ScopeSession, "ssl_server_not_before", ""}, + {ScopeGlobal, "ssl_session_cache_hits", "0"}, + {ScopeGlobal, "ssl_session_cache_misses", "0"}, + {ScopeGlobal, "ssl_session_cache_mode", "NONE"}, + {ScopeGlobal, "ssl_session_cache_overflows", "0"}, + {ScopeGlobal, "ssl_session_cache_size", "0"}, + {ScopeGlobal, "ssl_session_cache_timeouts", "0"}, + {ScopeGlobal | ScopeSession, "ssl_sessions_reused", "0"}, + {ScopeGlobal, "ssl_used_session_cache_entries", "0"}, + {ScopeGlobal | ScopeSession, "ssl_verify_depth", "0"}, + {ScopeGlobal | ScopeSession, "ssl_verify_mode", "0"}, + {ScopeGlobal | ScopeSession, "ssl_version", ""}, + {ScopeGlobal, "table_locks_immediate", "123"}, + {ScopeGlobal, "table_locks_waited", "0"}, + {ScopeGlobal | ScopeSession, "table_open_cache_hits", "28"}, + {ScopeGlobal | ScopeSession, "table_open_cache_misses", "108"}, + {ScopeGlobal | ScopeSession, "table_open_cache_overflows", "0"}, + {ScopeGlobal, "tc_log_max_pages_used", "0"}, + {ScopeGlobal, "tc_log_page_size", "0"}, + {ScopeGlobal, "tc_log_page_waits", "0"}, + {ScopeGlobal, "threads_cached", "0"}, + {ScopeGlobal, "threads_connected", "1"}, + {ScopeGlobal, "threads_created", "1"}, + {ScopeGlobal, "threads_running", "1"}, + {ScopeGlobal, "uptime", "15998"}, + {ScopeGlobal, "uptime_since_flush_status", "15998"}, +} diff --git a/sessionctx/variable/statusvar_test.go b/sessionctx/variable/statusvar_test.go new file mode 100644 index 0000000000..7cc532750b --- /dev/null +++ b/sessionctx/variable/statusvar_test.go @@ -0,0 +1,31 @@ +// Copyright 2015 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 variable + +import ( + . "github.com/pingcap/check" +) + +var _ = Suite(&testStatusVarSuite{}) + +type testStatusVarSuite struct { +} + +func (*testStatusVarSuite) TestStatusVar(c *C) { + f := GetStatusVar("aborted_clients") + c.Assert(f, NotNil) + + f = GetStatusVar("wrong-var-name") + c.Assert(f, IsNil) +} diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 50e554d53b..f97e490c84 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -574,12 +574,16 @@ const ( CollationConnection = "collation_connection" ) -// GlobalSysVarAccessor is the interface for accessing global scope system variables. -type GlobalSysVarAccessor interface { +// GlobalVarAccessor is the interface for accessing global scope system and status variables. +type GlobalVarAccessor interface { // GetGlobalSysVar gets the global system variable value for name. GetGlobalSysVar(ctx context.Context, name string) (string, error) // SetGlobalSysVar sets the global system variable name to value. SetGlobalSysVar(ctx context.Context, name string, value string) error + // GetGlobalStatusVar gets the global status variable value for name. + GetGlobalStatusVar(ctx context.Context, name string) (string, error) + // SetGlobalStatusVar sets the global status variable name to value. + SetGlobalStatusVar(ctx context.Context, name string, value string) error } // globalSysVarAccessorKeyType is a dummy type to avoid naming collision in context. @@ -592,14 +596,14 @@ func (k globalSysVarAccessorKeyType) String() string { const accessorKey globalSysVarAccessorKeyType = 0 -// BindGlobalSysVarAccessor binds global sysvar accessor to context. -func BindGlobalSysVarAccessor(ctx context.Context, accessor GlobalSysVarAccessor) { +// BindGlobalVarAccessor binds global var accessor to context. +func BindGlobalVarAccessor(ctx context.Context, accessor GlobalVarAccessor) { ctx.SetValue(accessorKey, accessor) } -// GetGlobalSysVarAccessor gets accessor from ctx. -func GetGlobalSysVarAccessor(ctx context.Context) GlobalSysVarAccessor { - v, ok := ctx.Value(accessorKey).(GlobalSysVarAccessor) +// GetGlobalVarAccessor gets accessor from ctx. +func GetGlobalVarAccessor(ctx context.Context) GlobalVarAccessor { + v, ok := ctx.Value(accessorKey).(GlobalVarAccessor) if !ok { panic("Miss global sysvar accessor") } diff --git a/stmt/stmt.go b/stmt/stmt.go index 3a88118139..0406a7da2a 100644 --- a/stmt/stmt.go +++ b/stmt/stmt.go @@ -55,6 +55,7 @@ const ( ShowWarnings ShowCharset ShowVariables + ShowStatus ShowCollation ShowCreateTable ShowGrants diff --git a/stmt/stmts/drop.go b/stmt/stmts/drop.go index bfd2d90e71..65d73d3afe 100644 --- a/stmt/stmts/drop.go +++ b/stmt/stmts/drop.go @@ -24,6 +24,8 @@ import ( "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/ddl" "github.com/pingcap/tidb/model" + "github.com/pingcap/tidb/mysql" + "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/rset" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/stmt" @@ -108,10 +110,34 @@ func (s *DropTableStmt) SetText(text string) { // Exec implements the stmt.Statement Exec interface. func (s *DropTableStmt) Exec(ctx context.Context) (rset.Recordset, error) { var notExistTables []string + is := sessionctx.GetDomain(ctx).InfoSchema() for _, ti := range s.TableIdents { - err := sessionctx.GetDomain(ctx).DDL().DropTable(ctx, ti.Full(ctx)) - // TODO: we should return special error for table not exist, checking "not exist" is not enough, - // because some other errors may contain this error string too. + fullti := ti.Full(ctx) + schema, ok := is.SchemaByName(fullti.Schema) + if !ok { + // TODO: we should return special error for table not exist, checking "not exist" is not enough, + // because some other errors may contain this error string too. + notExistTables = append(notExistTables, ti.String()) + continue + } + tb, err := is.TableByName(fullti.Schema, fullti.Name) + if err != nil && strings.HasSuffix(err.Error(), "not exist") { + notExistTables = append(notExistTables, ti.String()) + continue + } else if err != nil { + return nil, errors.Trace(err) + } + // Check Privilege + privChecker := privilege.GetPrivilegeChecker(ctx) + hasPriv, err := privChecker.Check(ctx, schema, tb.Meta(), mysql.DropPriv) + if err != nil { + return nil, errors.Trace(err) + } + if !hasPriv { + return nil, errors.Errorf("You do not have the privilege to drop table %s.%s.", ti.Schema, ti.Name) + } + + err = sessionctx.GetDomain(ctx).DDL().DropTable(ctx, fullti) if terror.ErrorEqual(err, ddl.ErrNotExists) || terror.DatabaseNotExists.Equal(err) { notExistTables = append(notExistTables, ti.String()) } else if err != nil { diff --git a/stmt/stmts/set.go b/stmt/stmts/set.go index b266b8c792..894d07417c 100644 --- a/stmt/stmts/set.go +++ b/stmt/stmts/set.go @@ -100,7 +100,7 @@ func (s *SetStmt) Exec(ctx context.Context) (_ rset.Recordset, err error) { log.Debug("Set sys/user variables") sessionVars := variable.GetSessionVars(ctx) - globalVars := variable.GetGlobalSysVarAccessor(ctx) + globalVars := variable.GetGlobalVarAccessor(ctx) for _, v := range s.Variables { // Variable is case insensitive, we use lower case. name := strings.ToLower(v.Name) diff --git a/store/localstore/kv_test.go b/store/localstore/kv_test.go index 67264d2071..fea59447d5 100644 --- a/store/localstore/kv_test.go +++ b/store/localstore/kv_test.go @@ -451,7 +451,8 @@ func (s *testKVSuite) TestConditionIfNotExist(c *C) { }() } wg.Wait() - c.Assert(success, Greater, int64(1)) + // At least one txn can success. + c.Assert(success, Greater, int64(0)) // Clean up txn, err := s.s.Begin() diff --git a/terror/terror.go b/terror/terror.go index 3845392d54..cfa6c9e6cd 100644 --- a/terror/terror.go +++ b/terror/terror.go @@ -28,6 +28,10 @@ var ( CommitNotInTransaction = ClassExecutor.New(CodeCommitNotInTransaction, "commit not in transaction") RollbackNotInTransaction = ClassExecutor.New(CodeRollbackNotInTransaction, "rollback not in transaction") + ExecResultIsEmpty = ClassExecutor.New(CodeExecResultIsEmpty, "exec result is empty") + + UnknownStatusVar = ClassVariable.New(CodeUnknownStatusVar, "unknown status variable") + UnknownSystemVar = ClassVariable.New(CodeUnknownSystemVar, "unknown system variable") ) // ErrCode represents a specific error type in a error class. @@ -44,6 +48,7 @@ const ( const ( CodeCommitNotInTransaction ErrCode = iota + 1 CodeRollbackNotInTransaction + CodeExecResultIsEmpty ) // KV error codes. @@ -52,6 +57,12 @@ const ( CodeNoDataForHandle ) +// Variable error codes. +const ( + CodeUnknownStatusVar ErrCode = iota + 1 + CodeUnknownSystemVar +) + // ErrClass represents a class of errors. type ErrClass int @@ -63,6 +74,7 @@ const ( ClassExecutor ClassKV ClassServer + ClassVariable // Add more as needed. ) diff --git a/util/mock/context.go b/util/mock/context.go index c5f4115a25..ae078bee19 100644 --- a/util/mock/context.go +++ b/util/mock/context.go @@ -16,11 +16,11 @@ package mock import ( "fmt" - "strings" "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/terror" ) var _ context.Context = (*Context)(nil) @@ -57,22 +57,39 @@ func (c *Context) FinishTxn(rollback bool) error { return nil } -// GetGlobalSysVar implements GlobalSysVarAccessor GetGlobalSysVar interface. -func (c *Context) GetGlobalSysVar(ctx context.Context, name string) (string, error) { - sysvars := variable.SysVars - v, ok := sysvars[strings.ToLower(name)] - if !ok { - return "", nil +// GetGlobalStatusVar implements GlobalVarAccessor GetGlobalStatusVar interface. +func (c *Context) GetGlobalStatusVar(ctx context.Context, name string) (string, error) { + v := variable.GetStatusVar(name) + if v == nil { + return "", terror.UnknownStatusVar.Gen("unknown status variable: %s", name) } return v.Value, nil } -// SetGlobalSysVar implements GlobalSysVarAccessor SetGlobalSysVar interface. +// SetGlobalStatusVar implements GlobalVarAccessor SetGlobalStatusVar interface. +func (c *Context) SetGlobalStatusVar(ctx context.Context, name string, value string) error { + v := variable.GetStatusVar(name) + if v == nil { + return terror.UnknownStatusVar.Gen("unknown status variable: %s", name) + } + v.Value = value + return nil +} + +// GetGlobalSysVar implements GlobalVarAccessor GetGlobalSysVar interface. +func (c *Context) GetGlobalSysVar(ctx context.Context, name string) (string, error) { + v := variable.GetSysVar(name) + if v == nil { + return "", terror.UnknownSystemVar.Gen("unknown sys variable: %s", name) + } + return v.Value, nil +} + +// SetGlobalSysVar implements GlobalVarAccessor SetGlobalSysVar interface. func (c *Context) SetGlobalSysVar(ctx context.Context, name string, value string) error { - sysvars := variable.SysVars - v, ok := sysvars[strings.ToLower(name)] - if !ok { - return fmt.Errorf("Unknown sys var: %s", name) + v := variable.GetSysVar(name) + if v == nil { + return terror.UnknownSystemVar.Gen("unknown sys variable: %s", name) } v.Value = value return nil diff --git a/util/types/helper.go b/util/types/helper.go index 4ab32208df..b9b0817f3b 100644 --- a/util/types/helper.go +++ b/util/types/helper.go @@ -21,19 +21,11 @@ import ( // RoundFloat uses default rounding mode, see http://www.gnu.org/software/libc/manual/html_node/Rounding.html // so we will choose the even number if the result is midway between two representable value. // e.g, 1.5 -> 2, 2.5 -> 2. -func RoundFloat(val float64) float64 { - v, frac := math.Modf(val) - if val >= 0.0 { - if frac > 0.5 || (frac == 0.5 && uint64(v)%2 != 0) { - v += 1.0 - } - } else { - if frac < -0.5 || (frac == -0.5 && uint64(v)%2 != 0) { - v -= 1.0 - } +func RoundFloat(f float64) float64 { + if math.Remainder(f, 1.0) < 0 { + return math.Ceil(f) } - - return v + return math.Floor(f) } func getMaxFloat(flen int, decimal int) float64 {