mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 03:18:24 +00:00 
			
		
		
		
	* Dropped unused codekit config * Integrated dynamic and static bindata for public * Ignore public bindata * Add a general generate make task * Integrated flexible public assets into web command * Updated vendoring, added all missiong govendor deps * Made the linter happy with the bindata and dynamic code * Moved public bindata definition to modules directory * Ignoring the new bindata path now * Updated to the new public modules import path * Updated public bindata command and drop the new prefix
		
			
				
	
	
		
			462 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			462 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2016 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 executor
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/juju/errors"
 | |
| 	"github.com/pingcap/tidb/ast"
 | |
| 	"github.com/pingcap/tidb/column"
 | |
| 	"github.com/pingcap/tidb/context"
 | |
| 	"github.com/pingcap/tidb/infoschema"
 | |
| 	"github.com/pingcap/tidb/model"
 | |
| 	"github.com/pingcap/tidb/mysql"
 | |
| 	"github.com/pingcap/tidb/privilege"
 | |
| 	"github.com/pingcap/tidb/sessionctx/variable"
 | |
| 	"github.com/pingcap/tidb/table"
 | |
| 	"github.com/pingcap/tidb/util/charset"
 | |
| 	"github.com/pingcap/tidb/util/types"
 | |
| )
 | |
| 
 | |
| // ShowExec represents a show executor.
 | |
| type ShowExec struct {
 | |
| 	Tp     ast.ShowStmtType // Databases/Tables/Columns/....
 | |
| 	DBName model.CIStr
 | |
| 	Table  *ast.TableName  // Used for showing columns.
 | |
| 	Column *ast.ColumnName // Used for `desc table column`.
 | |
| 	Flag   int             // Some flag parsed from sql, such as FULL.
 | |
| 	Full   bool
 | |
| 	User   string // Used for show grants.
 | |
| 
 | |
| 	// Used by show variables
 | |
| 	GlobalScope bool
 | |
| 
 | |
| 	fields []*ast.ResultField
 | |
| 	ctx    context.Context
 | |
| 	is     infoschema.InfoSchema
 | |
| 
 | |
| 	fetched bool
 | |
| 	rows    []*Row
 | |
| 	cursor  int
 | |
| }
 | |
| 
 | |
| // Fields implements Executor Fields interface.
 | |
| func (e *ShowExec) Fields() []*ast.ResultField {
 | |
| 	return e.fields
 | |
| }
 | |
| 
 | |
| // Next implements Execution Next interface.
 | |
| func (e *ShowExec) Next() (*Row, error) {
 | |
| 	if e.rows == nil {
 | |
| 		err := e.fetchAll()
 | |
| 		if err != nil {
 | |
| 			return nil, errors.Trace(err)
 | |
| 		}
 | |
| 	}
 | |
| 	if e.cursor >= len(e.rows) {
 | |
| 		return nil, nil
 | |
| 	}
 | |
| 	row := e.rows[e.cursor]
 | |
| 	for i, field := range e.fields {
 | |
| 		field.Expr.SetValue(row.Data[i].GetValue())
 | |
| 	}
 | |
| 	e.cursor++
 | |
| 	return row, nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchAll() error {
 | |
| 	switch e.Tp {
 | |
| 	case ast.ShowCharset:
 | |
| 		return e.fetchShowCharset()
 | |
| 	case ast.ShowCollation:
 | |
| 		return e.fetchShowCollation()
 | |
| 	case ast.ShowColumns:
 | |
| 		return e.fetchShowColumns()
 | |
| 	case ast.ShowCreateTable:
 | |
| 		return e.fetchShowCreateTable()
 | |
| 	case ast.ShowDatabases:
 | |
| 		return e.fetchShowDatabases()
 | |
| 	case ast.ShowEngines:
 | |
| 		return e.fetchShowEngines()
 | |
| 	case ast.ShowGrants:
 | |
| 		return e.fetchShowGrants()
 | |
| 	case ast.ShowIndex:
 | |
| 		return e.fetchShowIndex()
 | |
| 	case ast.ShowProcedureStatus:
 | |
| 		return e.fetchShowProcedureStatus()
 | |
| 	case ast.ShowStatus:
 | |
| 		return e.fetchShowStatus()
 | |
| 	case ast.ShowTables:
 | |
| 		return e.fetchShowTables()
 | |
| 	case ast.ShowTableStatus:
 | |
| 		return e.fetchShowTableStatus()
 | |
| 	case ast.ShowTriggers:
 | |
| 		return e.fetchShowTriggers()
 | |
| 	case ast.ShowVariables:
 | |
| 		return e.fetchShowVariables()
 | |
| 	case ast.ShowWarnings:
 | |
| 		// empty result
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowEngines() error {
 | |
| 	row := &Row{
 | |
| 		Data: types.MakeDatums(
 | |
| 			"InnoDB",
 | |
| 			"DEFAULT",
 | |
| 			"Supports transactions, row-level locking, and foreign keys",
 | |
| 			"YES",
 | |
| 			"YES",
 | |
| 			"YES",
 | |
| 		),
 | |
| 	}
 | |
| 	e.rows = append(e.rows, row)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowDatabases() error {
 | |
| 	dbs := e.is.AllSchemaNames()
 | |
| 	// TODO: let information_schema be the first database
 | |
| 	sort.Strings(dbs)
 | |
| 	for _, d := range dbs {
 | |
| 		e.rows = append(e.rows, &Row{Data: types.MakeDatums(d)})
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowTables() error {
 | |
| 	if !e.is.SchemaExists(e.DBName) {
 | |
| 		return errors.Errorf("Can not find DB: %s", e.DBName)
 | |
| 	}
 | |
| 	// sort for tables
 | |
| 	var tableNames []string
 | |
| 	for _, v := range e.is.SchemaTables(e.DBName) {
 | |
| 		tableNames = append(tableNames, v.Meta().Name.L)
 | |
| 	}
 | |
| 	sort.Strings(tableNames)
 | |
| 	for _, v := range tableNames {
 | |
| 		data := types.MakeDatums(v)
 | |
| 		if e.Full {
 | |
| 			// TODO: support "VIEW" later if we have supported view feature.
 | |
| 			// now, just use "BASE TABLE".
 | |
| 			data = append(data, types.NewDatum("BASE TABLE"))
 | |
| 		}
 | |
| 		e.rows = append(e.rows, &Row{Data: data})
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowTableStatus() error {
 | |
| 	if !e.is.SchemaExists(e.DBName) {
 | |
| 		return errors.Errorf("Can not find DB: %s", e.DBName)
 | |
| 	}
 | |
| 
 | |
| 	// sort for tables
 | |
| 	var tableNames []string
 | |
| 	for _, v := range e.is.SchemaTables(e.DBName) {
 | |
| 		tableNames = append(tableNames, v.Meta().Name.L)
 | |
| 	}
 | |
| 	sort.Strings(tableNames)
 | |
| 
 | |
| 	for _, v := range tableNames {
 | |
| 		now := mysql.CurrentTime(mysql.TypeDatetime)
 | |
| 		data := types.MakeDatums(v, "InnoDB", "10", "Compact", 100, 100, 100, 100, 100, 100, 100,
 | |
| 			now, now, now, "utf8_general_ci", "", "", "")
 | |
| 		e.rows = append(e.rows, &Row{Data: data})
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowColumns() error {
 | |
| 	tb, err := e.getTable()
 | |
| 	if err != nil {
 | |
| 		return errors.Trace(err)
 | |
| 	}
 | |
| 	cols := tb.Cols()
 | |
| 	for _, col := range cols {
 | |
| 		if e.Column != nil && e.Column.Name.L != col.Name.L {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		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 := &Row{}
 | |
| 		if e.Full {
 | |
| 			row.Data = types.MakeDatums(
 | |
| 				desc.Field,
 | |
| 				desc.Type,
 | |
| 				desc.Collation,
 | |
| 				desc.Null,
 | |
| 				desc.Key,
 | |
| 				desc.DefaultValue,
 | |
| 				desc.Extra,
 | |
| 				desc.Privileges,
 | |
| 				desc.Comment,
 | |
| 			)
 | |
| 		} else {
 | |
| 			row.Data = types.MakeDatums(
 | |
| 				desc.Field,
 | |
| 				desc.Type,
 | |
| 				desc.Null,
 | |
| 				desc.Key,
 | |
| 				desc.DefaultValue,
 | |
| 				desc.Extra,
 | |
| 			)
 | |
| 		}
 | |
| 		e.rows = append(e.rows, row)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowIndex() error {
 | |
| 	tb, err := e.getTable()
 | |
| 	if err != nil {
 | |
| 		return errors.Trace(err)
 | |
| 	}
 | |
| 	for _, idx := range tb.Indices() {
 | |
| 		for i, col := range idx.Columns {
 | |
| 			nonUniq := 1
 | |
| 			if idx.Unique {
 | |
| 				nonUniq = 0
 | |
| 			}
 | |
| 			var subPart interface{}
 | |
| 			if col.Length != types.UnspecifiedLength {
 | |
| 				subPart = col.Length
 | |
| 			}
 | |
| 			data := types.MakeDatums(
 | |
| 				tb.Meta().Name.O, // Table
 | |
| 				nonUniq,          // Non_unique
 | |
| 				idx.Name.O,       // Key_name
 | |
| 				i+1,              // Seq_in_index
 | |
| 				col.Name.O,       // Column_name
 | |
| 				"utf8_bin",       // Colation
 | |
| 				0,                // Cardinality
 | |
| 				subPart,          // Sub_part
 | |
| 				nil,              // Packed
 | |
| 				"YES",            // Null
 | |
| 				idx.Tp.String(),  // Index_type
 | |
| 				"",               // Comment
 | |
| 				idx.Comment,      // Index_comment
 | |
| 			)
 | |
| 			e.rows = append(e.rows, &Row{Data: data})
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowCharset() error {
 | |
| 	// See: http://dev.mysql.com/doc/refman/5.7/en/show-character-set.html
 | |
| 	descs := charset.GetAllCharsets()
 | |
| 	for _, desc := range descs {
 | |
| 		row := &Row{
 | |
| 			Data: types.MakeDatums(
 | |
| 				desc.Name,
 | |
| 				desc.Desc,
 | |
| 				desc.DefaultCollation,
 | |
| 				desc.Maxlen,
 | |
| 			),
 | |
| 		}
 | |
| 		e.rows = append(e.rows, row)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowVariables() error {
 | |
| 	sessionVars := variable.GetSessionVars(e.ctx)
 | |
| 	globalVars := variable.GetGlobalVarAccessor(e.ctx)
 | |
| 	for _, v := range variable.SysVars {
 | |
| 		var err error
 | |
| 		var value string
 | |
| 		if !e.GlobalScope {
 | |
| 			// Try to get Session Scope variable value first.
 | |
| 			sv, ok := sessionVars.Systems[v.Name]
 | |
| 			if ok {
 | |
| 				value = sv
 | |
| 			} else {
 | |
| 				// If session scope variable is not set, get the global scope value.
 | |
| 				value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name)
 | |
| 				if err != nil {
 | |
| 					return errors.Trace(err)
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name)
 | |
| 			if err != nil {
 | |
| 				return errors.Trace(err)
 | |
| 			}
 | |
| 		}
 | |
| 		row := &Row{Data: types.MakeDatums(v.Name, value)}
 | |
| 		e.rows = append(e.rows, row)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowStatus() error {
 | |
| 	statusVars, err := variable.GetStatusVars()
 | |
| 	if err != nil {
 | |
| 		return errors.Trace(err)
 | |
| 	}
 | |
| 	for status, v := range statusVars {
 | |
| 		if e.GlobalScope && v.Scope == variable.ScopeSession {
 | |
| 			continue
 | |
| 		}
 | |
| 		value, err := types.ToString(v.Value)
 | |
| 		if err != nil {
 | |
| 			return errors.Trace(err)
 | |
| 		}
 | |
| 		row := &Row{Data: types.MakeDatums(status, value)}
 | |
| 		e.rows = append(e.rows, row)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowCreateTable() error {
 | |
| 	tb, err := e.getTable()
 | |
| 	if err != nil {
 | |
| 		return errors.Trace(err)
 | |
| 	}
 | |
| 
 | |
| 	// TODO: let the result more like MySQL.
 | |
| 	var buf bytes.Buffer
 | |
| 	buf.WriteString(fmt.Sprintf("CREATE TABLE `%s` (\n", tb.Meta().Name.O))
 | |
| 	for i, col := range tb.Cols() {
 | |
| 		buf.WriteString(fmt.Sprintf("  `%s` %s", col.Name.O, col.GetTypeDesc()))
 | |
| 		if mysql.HasAutoIncrementFlag(col.Flag) {
 | |
| 			buf.WriteString(" NOT NULL AUTO_INCREMENT")
 | |
| 		} else {
 | |
| 			if mysql.HasNotNullFlag(col.Flag) {
 | |
| 				buf.WriteString(" NOT NULL")
 | |
| 			}
 | |
| 			switch col.DefaultValue {
 | |
| 			case nil:
 | |
| 				buf.WriteString(" DEFAULT NULL")
 | |
| 			case "CURRENT_TIMESTAMP":
 | |
| 				buf.WriteString(" DEFAULT CURRENT_TIMESTAMP")
 | |
| 			default:
 | |
| 				buf.WriteString(fmt.Sprintf(" DEFAULT '%v'", col.DefaultValue))
 | |
| 			}
 | |
| 
 | |
| 			if mysql.HasOnUpdateNowFlag(col.Flag) {
 | |
| 				buf.WriteString(" ON UPDATE CURRENT_TIMESTAMP")
 | |
| 			}
 | |
| 		}
 | |
| 		if i != len(tb.Cols())-1 {
 | |
| 			buf.WriteString(",\n")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if len(tb.Indices()) > 0 {
 | |
| 		buf.WriteString(",\n")
 | |
| 	}
 | |
| 
 | |
| 	for i, idx := range tb.Indices() {
 | |
| 		if idx.Primary {
 | |
| 			buf.WriteString("  PRIMARY KEY ")
 | |
| 		} else if idx.Unique {
 | |
| 			buf.WriteString(fmt.Sprintf("  UNIQUE KEY `%s` ", idx.Name.O))
 | |
| 		} else {
 | |
| 			buf.WriteString(fmt.Sprintf("  KEY `%s` ", idx.Name.O))
 | |
| 		}
 | |
| 
 | |
| 		cols := make([]string, 0, len(idx.Columns))
 | |
| 		for _, c := range idx.Columns {
 | |
| 			cols = append(cols, c.Name.O)
 | |
| 		}
 | |
| 		buf.WriteString(fmt.Sprintf("(`%s`)", strings.Join(cols, "`,`")))
 | |
| 		if i != len(tb.Indices())-1 {
 | |
| 			buf.WriteString(",\n")
 | |
| 		}
 | |
| 	}
 | |
| 	buf.WriteString("\n")
 | |
| 
 | |
| 	buf.WriteString(") ENGINE=InnoDB")
 | |
| 	if s := tb.Meta().Charset; len(s) > 0 {
 | |
| 		buf.WriteString(fmt.Sprintf(" DEFAULT CHARSET=%s", s))
 | |
| 	} else {
 | |
| 		buf.WriteString(" DEFAULT CHARSET=latin1")
 | |
| 	}
 | |
| 
 | |
| 	data := types.MakeDatums(tb.Meta().Name.O, buf.String())
 | |
| 	e.rows = append(e.rows, &Row{Data: data})
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowCollation() error {
 | |
| 	collations := charset.GetCollations()
 | |
| 	for _, v := range collations {
 | |
| 		isDefault := ""
 | |
| 		if v.IsDefault {
 | |
| 			isDefault = "Yes"
 | |
| 		}
 | |
| 		row := &Row{Data: types.MakeDatums(
 | |
| 			v.Name,
 | |
| 			v.CharsetName,
 | |
| 			v.ID,
 | |
| 			isDefault,
 | |
| 			"Yes",
 | |
| 			1,
 | |
| 		)}
 | |
| 		e.rows = append(e.rows, row)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowGrants() error {
 | |
| 	// Get checker
 | |
| 	checker := privilege.GetPrivilegeChecker(e.ctx)
 | |
| 	if checker == nil {
 | |
| 		return errors.New("Miss privilege checker!")
 | |
| 	}
 | |
| 	gs, err := checker.ShowGrants(e.ctx, e.User)
 | |
| 	if err != nil {
 | |
| 		return errors.Trace(err)
 | |
| 	}
 | |
| 	for _, g := range gs {
 | |
| 		data := types.MakeDatums(g)
 | |
| 		e.rows = append(e.rows, &Row{Data: data})
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowTriggers() error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) fetchShowProcedureStatus() error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (e *ShowExec) getTable() (table.Table, error) {
 | |
| 	if e.Table == nil {
 | |
| 		return nil, errors.New("table not found")
 | |
| 	}
 | |
| 	tb, ok := e.is.TableByID(e.Table.TableInfo.ID)
 | |
| 	if !ok {
 | |
| 		return nil, errors.Errorf("table %s not found", e.Table.Name)
 | |
| 	}
 | |
| 	return tb, nil
 | |
| }
 | |
| 
 | |
| // Close implements Executor Close interface.
 | |
| func (e *ShowExec) Close() error {
 | |
| 	return nil
 | |
| }
 |