From 49ae9493e32041402173b0dc36d6ea9ec954f984 Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 28 Sep 2015 10:49:31 +0800 Subject: [PATCH 1/3] stmts: ShowStmt uses SetCondition to binds like or where --- stmt/stmts/show.go | 10 ++++++++++ stmt/stmts/show_test.go | 1 + 2 files changed, 11 insertions(+) diff --git a/stmt/stmts/show.go b/stmt/stmts/show.go index f0ec8d71a1001..62f9d8eedebb8 100644 --- a/stmt/stmts/show.go +++ b/stmt/stmts/show.go @@ -93,3 +93,13 @@ func (s *ShowStmt) getDBName(ctx context.Context) string { // if s.DBName is empty, we should use current db name if possible. return db.GetCurrentSchema(ctx) } + +// SetCondition binds like or where expression. +func (s *ShowStmt) SetCondition(expr interface{}) { + switch x := expr.(type) { + case *expression.PatternLike: + s.Pattern = x + case expression.Expression: + s.Where = x + } +} diff --git a/stmt/stmts/show_test.go b/stmt/stmts/show_test.go index 24ca40d37e865..d3feeb85e66d4 100644 --- a/stmt/stmts/show_test.go +++ b/stmt/stmts/show_test.go @@ -44,4 +44,5 @@ func (s *testStmtSuite) TestShow(c *C) { c.Assert(stmtList, HasLen, 1) testStmt, ok = stmtList[0].(*stmts.ShowStmt) c.Assert(ok, IsTrue) + c.Assert(testStmt.Pattern, NotNil) } From 55a5b3937b2443ff98ff92bf8778debfd0345741 Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 28 Sep 2015 10:50:17 +0800 Subject: [PATCH 2/3] parser: use ShowLikeOrWhereOpt to reduce code --- parser/parser.y | 49 +++++++++++++++++-------------------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/parser/parser.y b/parser/parser.y index 3ff0283486d39..fc81f968f068b 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -437,6 +437,7 @@ import ( SetStmt "Set variable statement" ShowStmt "Show engines/databases/tables/columns/warnings statement" ShowDatabaseNameOpt "Show tables/columns statement database name option" + ShowLikeOrWhereOpt "Show like or where condition option" ShowTableIdentOpt "Show columns statement table name option" SignedLiteral "Literal or NumLiteral with sign" SimpleQualifiedIdent "Qualified identifier without *" @@ -3165,51 +3166,35 @@ ShowStmt: { $$ = &stmts.ShowStmt{Target: stmt.ShowWarnings} } -// See: https://dev.mysql.com/doc/refman/5.7/en/show-variables.html -// TODO: Support show variables with where clause. -| "SHOW" GlobalScope "VARIABLES" +| "SHOW" GlobalScope "VARIABLES" ShowLikeOrWhereOpt { - $$ = &stmts.ShowStmt{ - Target: stmt.ShowVariables, - GlobalScope: $2.(bool), - } - - } -| "SHOW" GlobalScope "VARIABLES" "LIKE" PrimaryExpression - { - $$ = &stmts.ShowStmt{ + stmt := &stmts.ShowStmt{ Target: stmt.ShowVariables, GlobalScope: $2.(bool), - Pattern: &expression.PatternLike{Pattern: $5.(expression.Expression)}, } + stmt.SetCondition($4) + $$ = stmt } -| "SHOW" GlobalScope "VARIABLES" "WHERE" Expression +| "SHOW" "COLLATION" ShowLikeOrWhereOpt { - $$ = &stmts.ShowStmt{ - Target: stmt.ShowVariables, - GlobalScope: $2.(bool), - Where: expression.Expr($5), + stmt := &stmts.ShowStmt{ + Target: stmt.ShowCollation, } + stmt.SetCondition($3) + $$ = stmt } -| "SHOW" "COLLATION" + +ShowLikeOrWhereOpt: { - $$ = &stmts.ShowStmt{ - Target: stmt.ShowCollation, - } + $$ = nil } -| "SHOW" "COLLATION" "LIKE" PrimaryExpression +| "LIKE" PrimaryExpression { - $$ = &stmts.ShowStmt{ - Target: stmt.ShowCollation, - Pattern: &expression.PatternLike{Pattern: $4.(expression.Expression)}, - } + $$ = &expression.PatternLike{Pattern: $2.(expression.Expression)} } -| "SHOW" "COLLATION" "WHERE" Expression +| "WHERE" Expression { - $$ = &stmts.ShowStmt{ - Target: stmt.ShowCollation, - Where: expression.Expr($4), - } + $$ = expression.Expr($2) } GlobalScope: From 358c9d06a8909c61a123338a651e0a4c1260f2c5 Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 28 Sep 2015 11:04:46 +0800 Subject: [PATCH 3/3] plans: split show into different functions --- plan/plans/show.go | 386 ++++++++++++++++++++++++--------------------- 1 file changed, 207 insertions(+), 179 deletions(-) diff --git a/plan/plans/show.go b/plan/plans/show.go index 2f59b6f8866e0..34aa475078408 100644 --- a/plan/plans/show.go +++ b/plan/plans/show.go @@ -139,215 +139,243 @@ func (s *ShowPlan) Next(ctx context.Context) (row *plan.Row, err error) { } func (s *ShowPlan) fetchAll(ctx context.Context) error { - // TODO split this function switch s.Target { case stmt.ShowEngines: - row := &plan.Row{ - Data: []interface{}{"InnoDB", "DEFAULT", "Supports transactions, row-level locking, and foreign keys", "YES", "YES", "YES"}, - } - s.rows = append(s.rows, row) + return s.fetchShowEngines(ctx) case stmt.ShowDatabases: - dbs := sessionctx.GetDomain(ctx).InfoSchema().AllSchemaNames() + return s.fetchShowDatabases(ctx) + case stmt.ShowTables: + return s.fetchShowTables(ctx) + case stmt.ShowColumns: + return s.fetchShowColumns(ctx) + case stmt.ShowWarnings: + // empty result + case stmt.ShowCharset: + return s.fetchShowCharset(ctx) + case stmt.ShowVariables: + return s.fetchShowVariables(ctx) + case stmt.ShowCollation: + return s.fetchShowCollation(ctx) + } + return nil +} + +// Close implements plan.Plan Close interface. +func (s *ShowPlan) Close() error { + s.rows = nil + s.cursor = 0 + return nil +} - // TODO: let information_schema be the first database - sort.Strings(dbs) +func (s *ShowPlan) evalCondition(ctx context.Context, m map[interface{}]interface{}) (bool, error) { + var cond expression.Expression + if s.Pattern != nil { + cond = s.Pattern + } else if s.Where != nil { + cond = s.Where + } - for _, d := range dbs { - s.rows = append(s.rows, &plan.Row{Data: []interface{}{d}}) - } - case stmt.ShowTables: - is := sessionctx.GetDomain(ctx).InfoSchema() - dbName := model.NewCIStr(s.DBName) - if !is.SchemaExists(dbName) { - return errors.Errorf("Can not find DB: %s", dbName) - } + if cond == nil { + return true, nil + } - // sort for tables - var tableNames []string - for _, v := range is.SchemaTables(dbName) { - tableNames = append(tableNames, v.TableName().L) + return expression.EvalBoolExpr(ctx, cond, m) +} + +func (s *ShowPlan) fetchShowColumns(ctx context.Context) error { + is := sessionctx.GetDomain(ctx).InfoSchema() + dbName := model.NewCIStr(s.DBName) + if !is.SchemaExists(dbName) { + return errors.Errorf("Can not find DB: %s", dbName) + } + tbName := model.NewCIStr(s.TableName) + tb, err := is.TableByName(dbName, tbName) + if err != nil { + return errors.Errorf("Can not find table: %s", s.TableName) + } + cols := tb.Cols() + + for _, col := range cols { + if !s.isColOK(col) { + continue } - sort.Strings(tableNames) + desc := column.NewColDesc(col) - for _, v := range tableNames { - data := []interface{}{v} - if s.Full { - // TODO: support "VIEW" later if we have supported view feature. - // now, just use "BASE TABLE". - data = append(data, "BASE TABLE") + // The FULL keyword causes the output to include the column collation and comments, + // as well as the privileges you have for each column. + row := &plan.Row{} + if s.Full { + row.Data = []interface{}{ + desc.Field, + desc.Type, + desc.Collation, + desc.Null, + desc.Key, + desc.DefaultValue, + desc.Extra, + desc.Privileges, + desc.Comment, + } + } else { + row.Data = []interface{}{ + desc.Field, + desc.Type, + desc.Null, + desc.Key, + desc.DefaultValue, + desc.Extra, } - s.rows = append(s.rows, &plan.Row{Data: data}) - } - case stmt.ShowColumns: - is := sessionctx.GetDomain(ctx).InfoSchema() - dbName := model.NewCIStr(s.DBName) - if !is.SchemaExists(dbName) { - return errors.Errorf("Can not find DB: %s", dbName) - } - tbName := model.NewCIStr(s.TableName) - tb, err := is.TableByName(dbName, tbName) - if err != nil { - return errors.Errorf("Can not find table: %s", s.TableName) } - cols := tb.Cols() + s.rows = append(s.rows, row) + } + return nil +} - for _, col := range cols { - if !s.isColOK(col) { - continue - } +func (s *ShowPlan) fetchShowCollation(ctx context.Context) error { + collations := charset.GetCollations() + m := map[interface{}]interface{}{} - desc := column.NewColDesc(col) - - // The FULL keyword causes the output to include the column collation and comments, - // as well as the privileges you have for each column. - row := &plan.Row{} - if s.Full { - row.Data = []interface{}{ - desc.Field, - desc.Type, - desc.Collation, - desc.Null, - desc.Key, - desc.DefaultValue, - desc.Extra, - desc.Privileges, - desc.Comment, - } - } else { - row.Data = []interface{}{ - desc.Field, - desc.Type, - desc.Null, - desc.Key, - desc.DefaultValue, - desc.Extra, + for _, v := range collations { + 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) { + switch { + case strings.EqualFold(name, "Collation"): + return v.Name, nil + case strings.EqualFold(name, "Charset"): + return v.CharsetName, nil + case strings.EqualFold(name, "Id"): + return v.ID, nil + case strings.EqualFold(name, "Default"): + if v.IsDefault { + return "Yes", nil + } + return "", nil + case strings.EqualFold(name, "Compiled"): + return "Yes", nil + case strings.EqualFold(name, "Sortlen"): + // TODO: add sort length in Collation + return 1, nil + default: + return nil, errors.Errorf("unknown field %s", name) } } - s.rows = append(s.rows, row) } - case stmt.ShowWarnings: - // empty result - case stmt.ShowCharset: - // See: http://dev.mysql.com/doc/refman/5.7/en/show-character-set.html - descs := charset.GetAllCharsets() - for _, desc := range descs { - row := &plan.Row{ - Data: []interface{}{desc.Name, desc.Desc, desc.DefaultCollation, desc.Maxlen}, - } - s.rows = append(s.rows, row) + + match, err := s.evalCondition(ctx, m) + if err != nil { + return errors.Trace(err) + } + if !match { + continue } - case stmt.ShowVariables: - sessionVars := variable.GetSessionVars(ctx) - for _, v := range variable.SysVars { - if s.Pattern != nil { - s.Pattern.Expr = expression.Value{Val: v.Name} - r, err := s.Pattern.Eval(ctx, nil) - if err != nil { - return errors.Trace(err) - } - match, ok := r.(bool) - if !ok { - return errors.Errorf("Eval like pattern error") - } - if !match { - continue - } - } else if s.Where != nil { - m := map[interface{}]interface{}{} - m[expression.ExprEvalIdentFunc] = func(name string) (interface{}, error) { - if strings.EqualFold(name, "Variable_name") { - return v.Name, nil - } + isDefault := "" + if v.IsDefault { + isDefault = "Yes" + } + row := &plan.Row{Data: []interface{}{v.Name, v.CharsetName, v.ID, isDefault, "Yes", 1}} + s.rows = append(s.rows, row) + } + return nil +} - return nil, errors.Errorf("unknown field %s", name) - } +func (s *ShowPlan) fetchShowTables(ctx context.Context) error { + is := sessionctx.GetDomain(ctx).InfoSchema() + dbName := model.NewCIStr(s.DBName) + if !is.SchemaExists(dbName) { + return errors.Errorf("Can not find DB: %s", dbName) + } - match, err := expression.EvalBoolExpr(ctx, s.Where, m) - if err != nil { - return errors.Trace(err) - } - if !match { - continue - } - } - value := v.Value - if !s.GlobalScope { - // Try to get Session Scope variable value - sv, ok := sessionVars.Systems[v.Name] - if ok { - value = sv - } - } - row := &plan.Row{Data: []interface{}{v.Name, value}} - s.rows = append(s.rows, row) + // sort for tables + var tableNames []string + for _, v := range is.SchemaTables(dbName) { + tableNames = append(tableNames, v.TableName().L) + } + + sort.Strings(tableNames) + + for _, v := range tableNames { + data := []interface{}{v} + if s.Full { + // TODO: support "VIEW" later if we have supported view feature. + // now, just use "BASE TABLE". + data = append(data, "BASE TABLE") } - case stmt.ShowCollation: - collations := charset.GetCollations() - for _, v := range collations { - if s.Pattern != nil { - s.Pattern.Expr = expression.Value{Val: v.Name} - r, err := s.Pattern.Eval(ctx, nil) - if err != nil { - return errors.Trace(err) - } - match, ok := r.(bool) - if !ok { - return errors.Errorf("Eval like pattern error") - } - if !match { - continue - } - } else if s.Where != nil { - m := map[interface{}]interface{}{} - - m[expression.ExprEvalIdentFunc] = func(name string) (interface{}, error) { - switch { - case strings.EqualFold(name, "Collation"): - return v.Name, nil - case strings.EqualFold(name, "Charset"): - return v.CharsetName, nil - case strings.EqualFold(name, "Id"): - return v.ID, nil - case strings.EqualFold(name, "Default"): - if v.IsDefault { - return "Yes", nil - } - return "", nil - case strings.EqualFold(name, "Compiled"): - return "Yes", nil - case strings.EqualFold(name, "Sortlen"): - // TODO: add sort length in Collation - return 1, nil - default: - return nil, errors.Errorf("unknown field %s", name) - } - } + s.rows = append(s.rows, &plan.Row{Data: data}) + } + return nil +} - match, err := expression.EvalBoolExpr(ctx, s.Where, m) - if err != nil { - return errors.Trace(err) - } - if !match { - continue +func (s *ShowPlan) fetchShowVariables(ctx context.Context) error { + sessionVars := variable.GetSessionVars(ctx) + m := map[interface{}]interface{}{} + + for _, v := range variable.SysVars { + 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 + } - isDefault := "" - if v.IsDefault { - isDefault = "Yes" + value := v.Value + if !s.GlobalScope { + // Try to get Session Scope variable value + sv, ok := sessionVars.Systems[v.Name] + if ok { + value = sv } - row := &plan.Row{Data: []interface{}{v.Name, v.CharsetName, v.ID, isDefault, "Yes", 1}} - s.rows = append(s.rows, row) } + row := &plan.Row{Data: []interface{}{v.Name, value}} + s.rows = append(s.rows, row) } return nil } -// Close implements plan.Plan Close interface. -func (s *ShowPlan) Close() error { - s.rows = nil - s.cursor = 0 +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() + for _, desc := range descs { + row := &plan.Row{ + Data: []interface{}{desc.Name, desc.Desc, desc.DefaultCollation, desc.Maxlen}, + } + s.rows = append(s.rows, row) + } + return nil +} + +func (s *ShowPlan) fetchShowEngines(ctx context.Context) error { + row := &plan.Row{ + Data: []interface{}{"InnoDB", "DEFAULT", "Supports transactions, row-level locking, and foreign keys", "YES", "YES", "YES"}, + } + s.rows = append(s.rows, row) + return nil +} + +func (s *ShowPlan) fetchShowDatabases(ctx context.Context) error { + dbs := sessionctx.GetDomain(ctx).InfoSchema().AllSchemaNames() + + // TODO: let information_schema be the first database + sort.Strings(dbs) + + for _, d := range dbs { + s.rows = append(s.rows, &plan.Row{Data: []interface{}{d}}) + } return nil }