diff --git a/enginetest/queries.go b/enginetest/queries.go index 8276ff956d..a5f177114e 100644 --- a/enginetest/queries.go +++ b/enginetest/queries.go @@ -5328,6 +5328,22 @@ var QueryTests = []QueryTest{ Query: `SELECT EXISTS (SELECT pk FROM one_pk WHERE pk > 4)`, Expected: []sql.Row{{false}}, }, + { + Query: `SHOW STATUS`, + Expected: []sql.Row{}, + }, + { + Query: `SHOW GLOBAL STATUS`, + Expected: []sql.Row{}, + }, + { + Query: `SHOW SESSION STATUS`, + Expected: []sql.Row{}, + }, + { + Query: `SHOW STATUS LIKE 'Bytes_received'`, + Expected: []sql.Row{}, + }, } var KeylessQueries = []QueryTest{ diff --git a/enginetest/variable_queries.go b/enginetest/variable_queries.go index f68dfff624..115f667563 100644 --- a/enginetest/variable_queries.go +++ b/enginetest/variable_queries.go @@ -125,6 +125,36 @@ var VariableQueries = []ScriptTest{ {"charset", "charset", "charset"}, }, }, + { + Name: "set character set", + SetUpScript: []string{ + `set character set utf8`, + }, + Query: "SELECT @@character_set_client, @@character_set_connection, @@character_set_results", + Expected: []sql.Row{ + {"utf8", "utf8mb4", "utf8"}, + }, + }, + { + Name: "set charset", + SetUpScript: []string{ + `set charset utf8`, + }, + Query: "SELECT @@character_set_client, @@character_set_connection, @@character_set_results", + Expected: []sql.Row{ + {"utf8", "utf8mb4", "utf8"}, + }, + }, + { + Name: "set charset quoted", + SetUpScript: []string{ + `set charset 'utf8'`, + }, + Query: "SELECT @@character_set_client, @@character_set_connection, @@character_set_results", + Expected: []sql.Row{ + {"utf8", "utf8mb4", "utf8"}, + }, + }, { Name: "set system variable to bareword", SetUpScript: []string{ diff --git a/sql/parse/parse.go b/sql/parse/parse.go index f95cde5781..5413137723 100644 --- a/sql/parse/parse.go +++ b/sql/parse/parse.go @@ -346,6 +346,34 @@ func convertSet(ctx *sql.Context, n *sqlparser.Set) (sql.Node, error) { }) } + // Special case: SET CHARACTER SET (CHARSET) expands to 3 different system variables. Although we do not support very + // many character sets, changing these variables should not have any effect currently as our character set support is + // mostly hardcoded to utf8mb4. + // See https://dev.mysql.com/doc/refman/5.7/en/set-character-set.html. + if isCharset(n.Exprs) { + csd, err := ctx.GetSessionVariable(ctx, "character_set_database") + if err != nil { + return nil, err + } + + return convertSet(ctx, &sqlparser.Set{ + Exprs: sqlparser.SetVarExprs{ + &sqlparser.SetVarExpr{ + Name: sqlparser.NewColName("character_set_client"), + Expr: n.Exprs[0].Expr, + }, + &sqlparser.SetVarExpr{ + Name: sqlparser.NewColName("character_set_results"), + Expr: n.Exprs[0].Expr, + }, + &sqlparser.SetVarExpr{ + Name: sqlparser.NewColName("character_set_connection"), + Expr: &sqlparser.SQLVal{Type: sqlparser.StrVal, Val: []byte(csd.(string))}, + }, + }, + }) + } + exprs, err := setExprsToExpressions(ctx, n.Exprs) if err != nil { return nil, err @@ -362,6 +390,14 @@ func isSetNames(exprs sqlparser.SetVarExprs) bool { return strings.ToLower(exprs[0].Name.String()) == "names" } +func isCharset(exprs sqlparser.SetVarExprs) bool { + if len(exprs) != 1 { + return false + } + + return strings.ToLower(exprs[0].Name.String()) == "charset" +} + func convertShow(ctx *sql.Context, s *sqlparser.Show, query string) (sql.Node, error) { showType := strings.ToLower(s.Type) switch showType { @@ -572,6 +608,12 @@ func convertShow(ctx *sql.Context, s *sqlparser.Show, query string) (sql.Node, e } return infoSchemaSelect, nil + case sqlparser.KeywordString(sqlparser.STATUS): + if s.Scope == sqlparser.GlobalStr { + return plan.NewShowStatus(plan.ShowStatusModifier_Global), nil + } + + return plan.NewShowStatus(plan.ShowStatusModifier_Session), nil default: unsupportedShow := fmt.Sprintf("SHOW %s", s.Type) return nil, ErrUnsupportedFeature.New(unsupportedShow) diff --git a/sql/plan/show_status.go b/sql/plan/show_status.go new file mode 100644 index 0000000000..20cf1feb49 --- /dev/null +++ b/sql/plan/show_status.go @@ -0,0 +1,75 @@ +// Copyright 2021 Dolthub, 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, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "github.com/dolthub/vitess/go/sqltypes" + + "github.com/dolthub/go-mysql-server/sql" +) + +// ShowStatus implements the SHOW STATUS MySQL command. +// TODO: This is just a stub implementation that returns an empty set. The actual functionality needs to be implemented +// in the future. +type ShowStatus struct { + modifier ShowStatusModifier +} + +var _ sql.Node = (*ShowStatus)(nil) + +type ShowStatusModifier byte + +const ( + ShowStatusModifier_Session ShowStatusModifier = iota + ShowStatusModifier_Global +) + +// NewShowStatus returns a new ShowStatus reference. +func NewShowStatus(modifier ShowStatusModifier) *ShowStatus { + return &ShowStatus{modifier: modifier} +} + +// Resolved implements sql.Node interface. +func (s *ShowStatus) Resolved() bool { + return true +} + +// String implements sql.Node interface. +func (s *ShowStatus) String() string { + return "SHOW STATUS" +} + +// Schema implements sql.Node interface. +func (s *ShowStatus) Schema() sql.Schema { + return sql.Schema{ + {Name: "Variable_name", Type: sql.MustCreateStringWithDefaults(sqltypes.VarChar, 64), Default: nil, Nullable: false}, + {Name: "Value", Type: sql.MustCreateStringWithDefaults(sqltypes.VarChar, 2048), Default: nil, Nullable: false}, + } +} + +// Children implements sql.Node interface. +func (s *ShowStatus) Children() []sql.Node { + return nil +} + +// RowIter implements sql.Node interface. +func (s *ShowStatus) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter, error) { + return sql.RowsToRowIter(), nil +} + +// WithChildren implements sql.Node interface. +func (s *ShowStatus) WithChildren(node ...sql.Node) (sql.Node, error) { + return NewShowStatus(s.modifier), nil +}