github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/executor/show.go (about)

     1  // Copyright 2016 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package executor
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  
    22  	"github.com/insionng/yougam/libraries/juju/errors"
    23  	"github.com/insionng/yougam/libraries/pingcap/tidb/ast"
    24  	"github.com/insionng/yougam/libraries/pingcap/tidb/column"
    25  	"github.com/insionng/yougam/libraries/pingcap/tidb/context"
    26  	"github.com/insionng/yougam/libraries/pingcap/tidb/infoschema"
    27  	"github.com/insionng/yougam/libraries/pingcap/tidb/model"
    28  	"github.com/insionng/yougam/libraries/pingcap/tidb/mysql"
    29  	"github.com/insionng/yougam/libraries/pingcap/tidb/privilege"
    30  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/variable"
    31  	"github.com/insionng/yougam/libraries/pingcap/tidb/table"
    32  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/charset"
    33  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/types"
    34  )
    35  
    36  // ShowExec represents a show executor.
    37  type ShowExec struct {
    38  	Tp     ast.ShowStmtType // Databases/Tables/Columns/....
    39  	DBName model.CIStr
    40  	Table  *ast.TableName  // Used for showing columns.
    41  	Column *ast.ColumnName // Used for `desc table column`.
    42  	Flag   int             // Some flag parsed from sql, such as FULL.
    43  	Full   bool
    44  	User   string // Used for show grants.
    45  
    46  	// Used by show variables
    47  	GlobalScope bool
    48  
    49  	fields []*ast.ResultField
    50  	ctx    context.Context
    51  	is     infoschema.InfoSchema
    52  
    53  	fetched bool
    54  	rows    []*Row
    55  	cursor  int
    56  }
    57  
    58  // Fields implements Executor Fields interface.
    59  func (e *ShowExec) Fields() []*ast.ResultField {
    60  	return e.fields
    61  }
    62  
    63  // Next implements Execution Next interface.
    64  func (e *ShowExec) Next() (*Row, error) {
    65  	if e.rows == nil {
    66  		err := e.fetchAll()
    67  		if err != nil {
    68  			return nil, errors.Trace(err)
    69  		}
    70  	}
    71  	if e.cursor >= len(e.rows) {
    72  		return nil, nil
    73  	}
    74  	row := e.rows[e.cursor]
    75  	for i, field := range e.fields {
    76  		field.Expr.SetValue(row.Data[i].GetValue())
    77  	}
    78  	e.cursor++
    79  	return row, nil
    80  }
    81  
    82  func (e *ShowExec) fetchAll() error {
    83  	switch e.Tp {
    84  	case ast.ShowCharset:
    85  		return e.fetchShowCharset()
    86  	case ast.ShowCollation:
    87  		return e.fetchShowCollation()
    88  	case ast.ShowColumns:
    89  		return e.fetchShowColumns()
    90  	case ast.ShowCreateTable:
    91  		return e.fetchShowCreateTable()
    92  	case ast.ShowDatabases:
    93  		return e.fetchShowDatabases()
    94  	case ast.ShowEngines:
    95  		return e.fetchShowEngines()
    96  	case ast.ShowGrants:
    97  		return e.fetchShowGrants()
    98  	case ast.ShowIndex:
    99  		return e.fetchShowIndex()
   100  	case ast.ShowProcedureStatus:
   101  		return e.fetchShowProcedureStatus()
   102  	case ast.ShowStatus:
   103  		return e.fetchShowStatus()
   104  	case ast.ShowTables:
   105  		return e.fetchShowTables()
   106  	case ast.ShowTableStatus:
   107  		return e.fetchShowTableStatus()
   108  	case ast.ShowTriggers:
   109  		return e.fetchShowTriggers()
   110  	case ast.ShowVariables:
   111  		return e.fetchShowVariables()
   112  	case ast.ShowWarnings:
   113  		// empty result
   114  	}
   115  	return nil
   116  }
   117  
   118  func (e *ShowExec) fetchShowEngines() error {
   119  	row := &Row{
   120  		Data: types.MakeDatums(
   121  			"InnoDB",
   122  			"DEFAULT",
   123  			"Supports transactions, row-level locking, and foreign keys",
   124  			"YES",
   125  			"YES",
   126  			"YES",
   127  		),
   128  	}
   129  	e.rows = append(e.rows, row)
   130  	return nil
   131  }
   132  
   133  func (e *ShowExec) fetchShowDatabases() error {
   134  	dbs := e.is.AllSchemaNames()
   135  	// TODO: let information_schema be the first database
   136  	sort.Strings(dbs)
   137  	for _, d := range dbs {
   138  		e.rows = append(e.rows, &Row{Data: types.MakeDatums(d)})
   139  	}
   140  	return nil
   141  }
   142  
   143  func (e *ShowExec) fetchShowTables() error {
   144  	if !e.is.SchemaExists(e.DBName) {
   145  		return errors.Errorf("Can not find DB: %s", e.DBName)
   146  	}
   147  	// sort for tables
   148  	var tableNames []string
   149  	for _, v := range e.is.SchemaTables(e.DBName) {
   150  		tableNames = append(tableNames, v.Meta().Name.L)
   151  	}
   152  	sort.Strings(tableNames)
   153  	for _, v := range tableNames {
   154  		data := types.MakeDatums(v)
   155  		if e.Full {
   156  			// TODO: support "VIEW" later if we have supported view feature.
   157  			// now, just use "BASE TABLE".
   158  			data = append(data, types.NewDatum("BASE TABLE"))
   159  		}
   160  		e.rows = append(e.rows, &Row{Data: data})
   161  	}
   162  	return nil
   163  }
   164  
   165  func (e *ShowExec) fetchShowTableStatus() error {
   166  	if !e.is.SchemaExists(e.DBName) {
   167  		return errors.Errorf("Can not find DB: %s", e.DBName)
   168  	}
   169  
   170  	// sort for tables
   171  	var tableNames []string
   172  	for _, v := range e.is.SchemaTables(e.DBName) {
   173  		tableNames = append(tableNames, v.Meta().Name.L)
   174  	}
   175  	sort.Strings(tableNames)
   176  
   177  	for _, v := range tableNames {
   178  		now := mysql.CurrentTime(mysql.TypeDatetime)
   179  		data := types.MakeDatums(v, "InnoDB", "10", "Compact", 100, 100, 100, 100, 100, 100, 100,
   180  			now, now, now, "utf8_general_ci", "", "", "")
   181  		e.rows = append(e.rows, &Row{Data: data})
   182  	}
   183  	return nil
   184  }
   185  
   186  func (e *ShowExec) fetchShowColumns() error {
   187  	tb, err := e.getTable()
   188  	if err != nil {
   189  		return errors.Trace(err)
   190  	}
   191  	cols := tb.Cols()
   192  	for _, col := range cols {
   193  		if e.Column != nil && e.Column.Name.L != col.Name.L {
   194  			continue
   195  		}
   196  
   197  		desc := column.NewColDesc(col)
   198  
   199  		// The FULL keyword causes the output to include the column collation and comments,
   200  		// as well as the privileges you have for each column.
   201  		row := &Row{}
   202  		if e.Full {
   203  			row.Data = types.MakeDatums(
   204  				desc.Field,
   205  				desc.Type,
   206  				desc.Collation,
   207  				desc.Null,
   208  				desc.Key,
   209  				desc.DefaultValue,
   210  				desc.Extra,
   211  				desc.Privileges,
   212  				desc.Comment,
   213  			)
   214  		} else {
   215  			row.Data = types.MakeDatums(
   216  				desc.Field,
   217  				desc.Type,
   218  				desc.Null,
   219  				desc.Key,
   220  				desc.DefaultValue,
   221  				desc.Extra,
   222  			)
   223  		}
   224  		e.rows = append(e.rows, row)
   225  	}
   226  	return nil
   227  }
   228  
   229  func (e *ShowExec) fetchShowIndex() error {
   230  	tb, err := e.getTable()
   231  	if err != nil {
   232  		return errors.Trace(err)
   233  	}
   234  	for _, idx := range tb.Indices() {
   235  		for i, col := range idx.Columns {
   236  			nonUniq := 1
   237  			if idx.Unique {
   238  				nonUniq = 0
   239  			}
   240  			var subPart interface{}
   241  			if col.Length != types.UnspecifiedLength {
   242  				subPart = col.Length
   243  			}
   244  			data := types.MakeDatums(
   245  				tb.Meta().Name.O, // Table
   246  				nonUniq,          // Non_unique
   247  				idx.Name.O,       // Key_name
   248  				i+1,              // Seq_in_index
   249  				col.Name.O,       // Column_name
   250  				"utf8_bin",       // Colation
   251  				0,                // Cardinality
   252  				subPart,          // Sub_part
   253  				nil,              // Packed
   254  				"YES",            // Null
   255  				idx.Tp.String(),  // Index_type
   256  				"",               // Comment
   257  				idx.Comment,      // Index_comment
   258  			)
   259  			e.rows = append(e.rows, &Row{Data: data})
   260  		}
   261  	}
   262  	return nil
   263  }
   264  
   265  func (e *ShowExec) fetchShowCharset() error {
   266  	// See: http://dev.mysql.com/doc/refman/5.7/en/show-character-set.html
   267  	descs := charset.GetAllCharsets()
   268  	for _, desc := range descs {
   269  		row := &Row{
   270  			Data: types.MakeDatums(
   271  				desc.Name,
   272  				desc.Desc,
   273  				desc.DefaultCollation,
   274  				desc.Maxlen,
   275  			),
   276  		}
   277  		e.rows = append(e.rows, row)
   278  	}
   279  	return nil
   280  }
   281  
   282  func (e *ShowExec) fetchShowVariables() error {
   283  	sessionVars := variable.GetSessionVars(e.ctx)
   284  	globalVars := variable.GetGlobalVarAccessor(e.ctx)
   285  	for _, v := range variable.SysVars {
   286  		var err error
   287  		var value string
   288  		if !e.GlobalScope {
   289  			// Try to get Session Scope variable value first.
   290  			sv, ok := sessionVars.Systems[v.Name]
   291  			if ok {
   292  				value = sv
   293  			} else {
   294  				// If session scope variable is not set, get the global scope value.
   295  				value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name)
   296  				if err != nil {
   297  					return errors.Trace(err)
   298  				}
   299  			}
   300  		} else {
   301  			value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name)
   302  			if err != nil {
   303  				return errors.Trace(err)
   304  			}
   305  		}
   306  		row := &Row{Data: types.MakeDatums(v.Name, value)}
   307  		e.rows = append(e.rows, row)
   308  	}
   309  	return nil
   310  }
   311  
   312  func (e *ShowExec) fetchShowStatus() error {
   313  	statusVars, err := variable.GetStatusVars()
   314  	if err != nil {
   315  		return errors.Trace(err)
   316  	}
   317  	for status, v := range statusVars {
   318  		if e.GlobalScope && v.Scope == variable.ScopeSession {
   319  			continue
   320  		}
   321  		switch v.Value.(type) {
   322  		case []interface{}, nil:
   323  			v.Value = fmt.Sprintf("%v", v.Value)
   324  		}
   325  		value, err := types.ToString(v.Value)
   326  		if err != nil {
   327  			return errors.Trace(err)
   328  		}
   329  		row := &Row{Data: types.MakeDatums(status, value)}
   330  		e.rows = append(e.rows, row)
   331  	}
   332  	return nil
   333  }
   334  
   335  func (e *ShowExec) fetchShowCreateTable() error {
   336  	tb, err := e.getTable()
   337  	if err != nil {
   338  		return errors.Trace(err)
   339  	}
   340  
   341  	// TODO: let the result more like MySQL.
   342  	var buf bytes.Buffer
   343  	buf.WriteString(fmt.Sprintf("CREATE TABLE `%s` (\n", tb.Meta().Name.O))
   344  	var pkCol *column.Col
   345  	for i, col := range tb.Cols() {
   346  		buf.WriteString(fmt.Sprintf("  `%s` %s", col.Name.O, col.GetTypeDesc()))
   347  		if mysql.HasAutoIncrementFlag(col.Flag) {
   348  			buf.WriteString(" NOT NULL AUTO_INCREMENT")
   349  		} else {
   350  			if mysql.HasNotNullFlag(col.Flag) {
   351  				buf.WriteString(" NOT NULL")
   352  			}
   353  			if !mysql.HasNoDefaultValueFlag(col.Flag) {
   354  				switch col.DefaultValue {
   355  				case nil:
   356  					buf.WriteString(" DEFAULT NULL")
   357  				case "CURRENT_TIMESTAMP":
   358  					buf.WriteString(" DEFAULT CURRENT_TIMESTAMP")
   359  				default:
   360  					buf.WriteString(fmt.Sprintf(" DEFAULT '%v'", col.DefaultValue))
   361  				}
   362  			}
   363  			if mysql.HasOnUpdateNowFlag(col.Flag) {
   364  				buf.WriteString(" ON UPDATE CURRENT_TIMESTAMP")
   365  			}
   366  		}
   367  		if len(col.Comment) > 0 {
   368  			buf.WriteString(fmt.Sprintf(" COMMENT '%s'", col.Comment))
   369  		}
   370  		if i != len(tb.Cols())-1 {
   371  			buf.WriteString(",\n")
   372  		}
   373  		if tb.Meta().PKIsHandle && mysql.HasPriKeyFlag(col.Flag) {
   374  			pkCol = col
   375  		}
   376  	}
   377  
   378  	if pkCol != nil {
   379  		// If PKIsHanle, pk info is not in tb.Indices(). We should handle it here.
   380  		buf.WriteString(",\n")
   381  		buf.WriteString(fmt.Sprintf(" PRIMARY KEY (`%s`) ", pkCol.Name.O))
   382  	}
   383  
   384  	if len(tb.Indices()) > 0 {
   385  		buf.WriteString(",\n")
   386  	}
   387  
   388  	for i, idx := range tb.Indices() {
   389  		if idx.Primary {
   390  			buf.WriteString("  PRIMARY KEY ")
   391  		} else if idx.Unique {
   392  			buf.WriteString(fmt.Sprintf("  UNIQUE KEY `%s` ", idx.Name.O))
   393  		} else {
   394  			buf.WriteString(fmt.Sprintf("  KEY `%s` ", idx.Name.O))
   395  		}
   396  
   397  		cols := make([]string, 0, len(idx.Columns))
   398  		for _, c := range idx.Columns {
   399  			cols = append(cols, c.Name.O)
   400  		}
   401  		buf.WriteString(fmt.Sprintf("(`%s`)", strings.Join(cols, "`,`")))
   402  		if i != len(tb.Indices())-1 {
   403  			buf.WriteString(",\n")
   404  		}
   405  	}
   406  
   407  	for _, fk := range tb.Meta().ForeignKeys {
   408  		if fk.State != model.StatePublic {
   409  			continue
   410  		}
   411  
   412  		buf.WriteString("\n")
   413  		cols := make([]string, 0, len(fk.Cols))
   414  		for _, c := range fk.Cols {
   415  			cols = append(cols, c.L)
   416  		}
   417  
   418  		refCols := make([]string, 0, len(fk.RefCols))
   419  		for _, c := range fk.Cols {
   420  			refCols = append(refCols, c.L)
   421  		}
   422  
   423  		buf.WriteString(fmt.Sprintf("  CONSTRAINT `%s` FOREIGN KEY (`%s`)", fk.Name.L, strings.Join(cols, "`,`")))
   424  		buf.WriteString(fmt.Sprintf(" REFERENCES `%s` (`%s`)", fk.RefTable.L, strings.Join(refCols, "`,`")))
   425  
   426  		if ast.ReferOptionType(fk.OnDelete) != ast.ReferOptionNoOption {
   427  			buf.WriteString(fmt.Sprintf(" ON DELETE %s", ast.ReferOptionType(fk.OnDelete)))
   428  		}
   429  
   430  		if ast.ReferOptionType(fk.OnUpdate) != ast.ReferOptionNoOption {
   431  			buf.WriteString(fmt.Sprintf(" ON UPDATE %s", ast.ReferOptionType(fk.OnUpdate)))
   432  		}
   433  	}
   434  	buf.WriteString("\n")
   435  
   436  	buf.WriteString(") ENGINE=InnoDB")
   437  	if s := tb.Meta().Charset; len(s) > 0 {
   438  		buf.WriteString(fmt.Sprintf(" DEFAULT CHARSET=%s", s))
   439  	}
   440  
   441  	if tb.Meta().AutoIncID > 0 {
   442  		buf.WriteString(fmt.Sprintf(" AUTO_INCREMENT=%d", tb.Meta().AutoIncID))
   443  	}
   444  
   445  	if len(tb.Meta().Comment) > 0 {
   446  		buf.WriteString(fmt.Sprintf(" COMMENT='%s'", tb.Meta().Comment))
   447  	}
   448  
   449  	data := types.MakeDatums(tb.Meta().Name.O, buf.String())
   450  	e.rows = append(e.rows, &Row{Data: data})
   451  	return nil
   452  }
   453  
   454  func (e *ShowExec) fetchShowCollation() error {
   455  	collations := charset.GetCollations()
   456  	for _, v := range collations {
   457  		isDefault := ""
   458  		if v.IsDefault {
   459  			isDefault = "Yes"
   460  		}
   461  		row := &Row{Data: types.MakeDatums(
   462  			v.Name,
   463  			v.CharsetName,
   464  			v.ID,
   465  			isDefault,
   466  			"Yes",
   467  			1,
   468  		)}
   469  		e.rows = append(e.rows, row)
   470  	}
   471  	return nil
   472  }
   473  
   474  func (e *ShowExec) fetchShowGrants() error {
   475  	// Get checker
   476  	checker := privilege.GetPrivilegeChecker(e.ctx)
   477  	if checker == nil {
   478  		return errors.New("Miss privilege checker!")
   479  	}
   480  	gs, err := checker.ShowGrants(e.ctx, e.User)
   481  	if err != nil {
   482  		return errors.Trace(err)
   483  	}
   484  	for _, g := range gs {
   485  		data := types.MakeDatums(g)
   486  		e.rows = append(e.rows, &Row{Data: data})
   487  	}
   488  	return nil
   489  }
   490  
   491  func (e *ShowExec) fetchShowTriggers() error {
   492  	return nil
   493  }
   494  
   495  func (e *ShowExec) fetchShowProcedureStatus() error {
   496  	return nil
   497  }
   498  
   499  func (e *ShowExec) getTable() (table.Table, error) {
   500  	if e.Table == nil {
   501  		return nil, errors.New("table not found")
   502  	}
   503  	tb, ok := e.is.TableByID(e.Table.TableInfo.ID)
   504  	if !ok {
   505  		return nil, errors.Errorf("table %s not found", e.Table.Name)
   506  	}
   507  	return tb, nil
   508  }
   509  
   510  // Close implements Executor Close interface.
   511  func (e *ShowExec) Close() error {
   512  	return nil
   513  }