github.com/dolthub/go-mysql-server@v0.18.0/sql/rowexec/show.go (about)

     1  // Copyright 2023 Dolthub, 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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package rowexec
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"sort"
    23  	"strings"
    24  
    25  	gmstime "github.com/dolthub/go-mysql-server/internal/time"
    26  	"github.com/dolthub/go-mysql-server/sql"
    27  	"github.com/dolthub/go-mysql-server/sql/mysql_db"
    28  	"github.com/dolthub/go-mysql-server/sql/plan"
    29  	"github.com/dolthub/go-mysql-server/sql/types"
    30  )
    31  
    32  func (b *BaseBuilder) buildShowCharset(ctx *sql.Context, n *plan.ShowCharset, row sql.Row) (sql.RowIter, error) {
    33  	//TODO: use the information_schema table instead, currently bypassing it to show currently-implemented charsets
    34  	//ri, err := sc.CharacterSetTable.RowIter(ctx, row)
    35  	//if err != nil {
    36  	//	return nil, err
    37  	//}
    38  	//return &showCharsetIter{originalIter: ri}, nil
    39  
    40  	var rows []sql.Row
    41  	iter := sql.NewCharacterSetsIterator()
    42  	for charset, ok := iter.Next(); ok; charset, ok = iter.Next() {
    43  		if charset.Encoder != nil && charset.BinaryCollation.Sorter() != nil && charset.DefaultCollation.Sorter() != nil {
    44  			rows = append(rows, sql.Row{
    45  				charset.Name,
    46  				charset.Description,
    47  				charset.DefaultCollation.String(),
    48  				uint64(charset.MaxLength),
    49  			})
    50  		}
    51  	}
    52  	return sql.RowsToRowIter(rows...), nil
    53  }
    54  
    55  func (b *BaseBuilder) buildDescribeQuery(ctx *sql.Context, n *plan.DescribeQuery, row sql.Row) (sql.RowIter, error) {
    56  	if n.Format.Analyze {
    57  		if !n.IsReadOnly() {
    58  			return nil, errors.New("cannot analyze statement that could have side effects")
    59  		}
    60  		// Iterate over the child until its exhausted, in order to populate the stats.
    61  		childIter, err := b.Build(ctx, n.Child, row)
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  		for {
    66  			_, err := childIter.Next(ctx)
    67  			if err == io.EOF {
    68  				break
    69  			}
    70  			if err != nil {
    71  				return nil, err
    72  			}
    73  		}
    74  	}
    75  
    76  	var rows []sql.Row
    77  	formatString := sql.Describe(n.Child, n.Format)
    78  
    79  	for _, l := range strings.Split(formatString, "\n") {
    80  		if strings.TrimSpace(l) != "" {
    81  			rows = append(rows, sql.NewRow(l))
    82  		}
    83  	}
    84  	return sql.RowsToRowIter(rows...), nil
    85  }
    86  
    87  func (b *BaseBuilder) buildShowWarnings(ctx *sql.Context, n plan.ShowWarnings, row sql.Row) (sql.RowIter, error) {
    88  	var rows []sql.Row
    89  	for _, w := range n {
    90  		rows = append(rows, sql.NewRow(w.Level, w.Code, w.Message))
    91  	}
    92  	return sql.RowsToRowIter(rows...), nil
    93  }
    94  
    95  func (b *BaseBuilder) buildShowProcessList(ctx *sql.Context, n *plan.ShowProcessList, row sql.Row) (sql.RowIter, error) {
    96  	processes := ctx.ProcessList.Processes()
    97  	var rows = make([]sql.Row, len(processes))
    98  
    99  	for i, proc := range processes {
   100  		var status []string
   101  		var names []string
   102  		for name := range proc.Progress {
   103  			names = append(names, name)
   104  		}
   105  		sort.Strings(names)
   106  
   107  		for _, name := range names {
   108  			progress := proc.Progress[name]
   109  
   110  			printer := sql.NewTreePrinter()
   111  			_ = printer.WriteNode("\n" + progress.String())
   112  			children := []string{}
   113  			for _, partitionProgress := range progress.PartitionsProgress {
   114  				children = append(children, partitionProgress.String())
   115  			}
   116  			sort.Strings(children)
   117  			_ = printer.WriteChildren(children...)
   118  
   119  			status = append(status, printer.String())
   120  		}
   121  
   122  		if len(status) == 0 && proc.Command == sql.ProcessCommandQuery {
   123  			status = []string{"running"}
   124  		}
   125  
   126  		rows[i] = process{
   127  			id:      int64(proc.Connection),
   128  			user:    proc.User,
   129  			time:    int64(proc.Seconds()),
   130  			state:   strings.Join(status, ""),
   131  			command: string(proc.Command),
   132  			host:    proc.Host,
   133  			info:    proc.Query,
   134  			db:      proc.Database,
   135  		}.toRow()
   136  	}
   137  
   138  	return sql.RowsToRowIter(rows...), nil
   139  }
   140  
   141  func (b *BaseBuilder) buildShowTableStatus(ctx *sql.Context, n *plan.ShowTableStatus, row sql.Row) (sql.RowIter, error) {
   142  	tables, err := n.Database().GetTableNames(ctx)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	var rows = make([]sql.Row, len(tables))
   148  
   149  	for i, tName := range tables {
   150  		table, _, err := n.Catalog.Table(ctx, n.Database().Name(), tName)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  
   155  		var numRows uint64
   156  		var dataLength uint64
   157  
   158  		if st, ok := table.(sql.StatisticsTable); ok {
   159  			numRows, _, err = st.RowCount(ctx)
   160  			if err != nil {
   161  				return nil, err
   162  			}
   163  
   164  			dataLength, err = st.DataLength(ctx)
   165  			if err != nil {
   166  				return nil, err
   167  			}
   168  		}
   169  
   170  		rows[i] = tableToStatusRow(tName, numRows, dataLength, table.Collation())
   171  	}
   172  
   173  	return sql.RowsToRowIter(rows...), nil
   174  }
   175  
   176  func (b *BaseBuilder) buildShowTables(ctx *sql.Context, n *plan.ShowTables, row sql.Row) (sql.RowIter, error) {
   177  	var tableNames []string
   178  
   179  	// TODO: this entire analysis should really happen in the analyzer, as opposed to at execution time
   180  	if n.AsOf() != nil {
   181  		if vdb, ok := n.Database().(sql.VersionedDatabase); ok {
   182  			asOf, err := n.AsOf().Eval(ctx, nil)
   183  			if err != nil {
   184  				return nil, err
   185  			}
   186  
   187  			tableNames, err = vdb.GetTableNamesAsOf(ctx, asOf)
   188  			if err != nil {
   189  				return nil, err
   190  			}
   191  		} else {
   192  			return nil, sql.ErrAsOfNotSupported.New(n.Database().Name())
   193  		}
   194  	} else {
   195  		var err error
   196  		tableNames, err = n.Database().GetTableNames(ctx)
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  	}
   201  
   202  	sort.Strings(tableNames)
   203  
   204  	var rows []sql.Row
   205  	for _, tableName := range tableNames {
   206  		row := sql.Row{tableName}
   207  		if n.Full {
   208  			row = append(row, "BASE TABLE")
   209  		}
   210  		rows = append(rows, row)
   211  	}
   212  
   213  	// TODO: currently there is no way to see views AS OF a particular time
   214  	db := n.Database()
   215  	if vdb, ok := db.(sql.ViewDatabase); ok {
   216  		views, err := vdb.AllViews(ctx)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  		for _, view := range views {
   221  			row := sql.Row{view.Name}
   222  			if n.Full {
   223  				row = append(row, "VIEW")
   224  			}
   225  			rows = append(rows, row)
   226  		}
   227  	}
   228  
   229  	for _, view := range ctx.GetViewRegistry().ViewsInDatabase(db.Name()) {
   230  		row := sql.Row{view.Name()}
   231  		if n.Full {
   232  			row = append(row, "VIEW")
   233  		}
   234  		rows = append(rows, row)
   235  	}
   236  
   237  	sort.Slice(rows, func(i, j int) bool {
   238  		return rows[i][0].(string) < rows[j][0].(string)
   239  	})
   240  
   241  	return sql.RowsToRowIter(rows...), nil
   242  }
   243  
   244  func (b *BaseBuilder) buildShowStatus(ctx *sql.Context, n *plan.ShowStatus, row sql.Row) (sql.RowIter, error) {
   245  	var names []string
   246  	for name := range sql.SystemVariables.NewSessionMap() {
   247  		names = append(names, name)
   248  	}
   249  	sort.Strings(names)
   250  
   251  	var rows []sql.Row
   252  	for _, name := range names {
   253  		sysVar, val, ok := sql.SystemVariables.GetGlobal(name)
   254  		if !ok {
   255  			return nil, fmt.Errorf("missing system variable %s", name)
   256  		}
   257  
   258  		if n.Modifier == plan.ShowStatusModifier_Session && sysVar.Scope == sql.SystemVariableScope_Global ||
   259  			n.Modifier == plan.ShowStatusModifier_Global && sysVar.Scope == sql.SystemVariableScope_Session {
   260  			continue
   261  		}
   262  
   263  		rows = append(rows, sql.Row{name, val})
   264  	}
   265  
   266  	return sql.RowsToRowIter(rows...), nil
   267  }
   268  
   269  func (b *BaseBuilder) buildShowCreateProcedure(ctx *sql.Context, n *plan.ShowCreateProcedure, row sql.Row) (sql.RowIter, error) {
   270  	characterSetClient, err := ctx.GetSessionVariable(ctx, "character_set_client")
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	collationConnection, err := ctx.GetSessionVariable(ctx, "collation_connection")
   275  	if err != nil {
   276  		return nil, err
   277  	}
   278  	collationServer, err := ctx.GetSessionVariable(ctx, "collation_server")
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  
   283  	if n.ExternalStoredProcedure != nil {
   284  		// If an external stored procedure has been plugged in by the analyzer, use that
   285  		fakeCreateProcedureStmt := n.ExternalStoredProcedure.FakeCreateProcedureStmt()
   286  		return sql.RowsToRowIter(sql.Row{
   287  			n.ExternalStoredProcedure.Name, // Procedure
   288  			"",                             // sql_mode
   289  			fakeCreateProcedureStmt,        // Create Procedure
   290  			characterSetClient,             // character_set_client
   291  			collationConnection,            // collation_connection
   292  			collationServer,                // Database Collation
   293  		}), nil
   294  	} else {
   295  		// Otherwise, search the StoredProcedureDatabase for a user-created stored procedure
   296  		procedureDb, ok := n.Database().(sql.StoredProcedureDatabase)
   297  		if !ok {
   298  			return nil, sql.ErrStoredProceduresNotSupported.New(n.Database().Name())
   299  		}
   300  		procedures, err := procedureDb.GetStoredProcedures(ctx)
   301  		if err != nil {
   302  			return nil, err
   303  		}
   304  		for _, procedure := range procedures {
   305  			if strings.ToLower(procedure.Name) == n.ProcedureName {
   306  				return sql.RowsToRowIter(sql.Row{
   307  					procedure.Name,            // Procedure
   308  					"",                        // sql_mode
   309  					procedure.CreateStatement, // Create Procedure
   310  					characterSetClient,        // character_set_client
   311  					collationConnection,       // collation_connection
   312  					collationServer,           // Database Collation
   313  				}), nil
   314  			}
   315  		}
   316  		return nil, sql.ErrStoredProcedureDoesNotExist.New(n.ProcedureName)
   317  	}
   318  }
   319  
   320  func (b *BaseBuilder) buildShowCreateDatabase(ctx *sql.Context, n *plan.ShowCreateDatabase, row sql.Row) (sql.RowIter, error) {
   321  	var name = n.Database().Name()
   322  
   323  	var buf bytes.Buffer
   324  
   325  	buf.WriteString("CREATE DATABASE ")
   326  	if n.IfNotExists {
   327  		buf.WriteString("/*!32312 IF NOT EXISTS*/ ")
   328  	}
   329  
   330  	buf.WriteRune('`')
   331  	buf.WriteString(name)
   332  	buf.WriteRune('`')
   333  	buf.WriteString(fmt.Sprintf(
   334  		" /*!40100 DEFAULT CHARACTER SET %s COLLATE %s */",
   335  		sql.Collation_Default.CharacterSet().String(),
   336  		sql.Collation_Default.String(),
   337  	))
   338  
   339  	return sql.RowsToRowIter(
   340  		sql.NewRow(name, buf.String()),
   341  	), nil
   342  }
   343  
   344  func (b *BaseBuilder) buildShowPrivileges(ctx *sql.Context, n *plan.ShowPrivileges, row sql.Row) (sql.RowIter, error) {
   345  	return sql.RowsToRowIter(
   346  		sql.Row{"Alter", "Tables", "To alter the table"},
   347  		sql.Row{"Alter routine", "Functions,Procedures", "To alter or drop stored functions/procedures"},
   348  		sql.Row{"Create", "Databases,Tables,Indexes", "To create new databases and tables"},
   349  		sql.Row{"Create routine", "Databases", "To use CREATE FUNCTION/PROCEDURE"},
   350  		sql.Row{"Create role", "Server Admin", "To create new roles"},
   351  		sql.Row{"Create temporary tables", "Databases", "To use CREATE TEMPORARY TABLE"},
   352  		sql.Row{"Create view", "Tables", "To create new views"},
   353  		sql.Row{"Create user", "Server Admin", "To create new users"},
   354  		sql.Row{"Delete", "Tables", "To delete existing rows"},
   355  		sql.Row{"Drop", "Databases,Tables", "To drop databases, tables, and views"},
   356  		sql.Row{"Drop role", "Server Admin", "To drop roles"},
   357  		sql.Row{"Event", "Server Admin", "To create, alter, drop and execute events"},
   358  		sql.Row{"Execute", "Functions,Procedures", "To execute stored routines"},
   359  		sql.Row{"File", "File access on server", "To read and write files on the server"},
   360  		sql.Row{"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"},
   361  		sql.Row{"Index", "Tables", "To create or drop indexes"},
   362  		sql.Row{"Insert", "Tables", "To insert data into tables"},
   363  		sql.Row{"Lock tables", "Databases", "To use LOCK TABLES (together with SELECT privilege)"},
   364  		sql.Row{"Process", "Server Admin", "To view the plain text of currently executing queries"},
   365  		sql.Row{"Proxy", "Server Admin", "To make proxy user possible"},
   366  		sql.Row{"References", "Databases,Tables", "To have references on tables"},
   367  		sql.Row{"Reload", "Server Admin", "To reload or refresh tables, logs and privileges"},
   368  		sql.Row{"Replication client", "Server Admin", "To ask where the slave or master servers are"},
   369  		sql.Row{"Replication slave", "Server Admin", "To read binary log events from the master"},
   370  		sql.Row{"Select", "Tables", "To retrieve rows from table"},
   371  		sql.Row{"Show databases", "Server Admin", "To see all databases with SHOW DATABASES"},
   372  		sql.Row{"Show view", "Tables", "To see views with SHOW CREATE VIEW"},
   373  		sql.Row{"Shutdown", "Server Admin", "To shut down the server"},
   374  		sql.Row{"Super", "Server Admin", "To use KILL thread, SET GLOBAL, CHANGE MASTER, etc."},
   375  		sql.Row{"Trigger", "Tables", "To use triggers"},
   376  		sql.Row{"Create tablespace", "Server Admin", "To create/alter/drop tablespaces"},
   377  		sql.Row{"Update", "Tables", "To update existing rows"},
   378  		sql.Row{"Usage", "Server Admin", "No privileges - allow connect only"},
   379  		sql.Row{"ENCRYPTION_KEY_ADMIN", "Server Admin", ""},
   380  		sql.Row{"INNODB_REDO_LOG_ARCHIVE", "Server Admin", ""},
   381  		sql.Row{"REPLICATION_APPLIER", "Server Admin", ""},
   382  		sql.Row{"INNODB_REDO_LOG_ENABLE", "Server Admin", ""},
   383  		sql.Row{"SET_USER_ID", "Server Admin", ""},
   384  		sql.Row{"SERVICE_CONNECTION_ADMIN", "Server Admin", ""},
   385  		sql.Row{"GROUP_REPLICATION_ADMIN", "Server Admin", ""},
   386  		sql.Row{"AUDIT_ABORT_EXEMPT", "Server Admin", ""},
   387  		sql.Row{"GROUP_REPLICATION_STREAM", "Server Admin", ""},
   388  		sql.Row{"CLONE_ADMIN", "Server Admin", ""},
   389  		sql.Row{"SYSTEM_USER", "Server Admin", ""},
   390  		sql.Row{"AUTHENTICATION_POLICY_ADMIN", "Server Admin", ""},
   391  		sql.Row{"SHOW_ROUTINE", "Server Admin", ""},
   392  		sql.Row{"BACKUP_ADMIN", "Server Admin", ""},
   393  		sql.Row{"CONNECTION_ADMIN", "Server Admin", ""},
   394  		sql.Row{"PERSIST_RO_VARIABLES_ADMIN", "Server Admin", ""},
   395  		sql.Row{"RESOURCE_GROUP_ADMIN", "Server Admin", ""},
   396  		sql.Row{"SESSION_VARIABLES_ADMIN", "Server Admin", ""},
   397  		sql.Row{"SYSTEM_VARIABLES_ADMIN", "Server Admin", ""},
   398  		sql.Row{"APPLICATION_PASSWORD_ADMIN", "Server Admin", ""},
   399  		sql.Row{"FLUSH_OPTIMIZER_COSTS", "Server Admin", ""},
   400  		sql.Row{"AUDIT_ADMIN", "Server Admin", ""},
   401  		sql.Row{"BINLOG_ADMIN", "Server Admin", ""},
   402  		sql.Row{"BINLOG_ENCRYPTION_ADMIN", "Server Admin", ""},
   403  		sql.Row{"FLUSH_STATUS", "Server Admin", ""},
   404  		sql.Row{"FLUSH_TABLES", "Server Admin", ""},
   405  		sql.Row{"FLUSH_USER_RESOURCES", "Server Admin", ""},
   406  		sql.Row{"XA_RECOVER_ADMIN", "Server Admin", ""},
   407  		sql.Row{"PASSWORDLESS_USER_ADMIN", "Server Admin", ""},
   408  		sql.Row{"TABLE_ENCRYPTION_ADMIN", "Server Admin", ""},
   409  		sql.Row{"ROLE_ADMIN", "Server Admin", ""},
   410  		sql.Row{"REPLICATION_SLAVE_ADMIN", "Server Admin", ""},
   411  		sql.Row{"RESOURCE_GROUP_USER", "Server Admin", ""},
   412  	), nil
   413  }
   414  
   415  func (b *BaseBuilder) buildShowCreateTrigger(ctx *sql.Context, n *plan.ShowCreateTrigger, row sql.Row) (sql.RowIter, error) {
   416  	triggerDb, ok := n.Database().(sql.TriggerDatabase)
   417  	if !ok {
   418  		return nil, sql.ErrTriggersNotSupported.New(n.Database().Name())
   419  	}
   420  	triggers, err := triggerDb.GetTriggers(ctx)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  	for _, trigger := range triggers {
   425  		if strings.ToLower(trigger.Name) == n.TriggerName {
   426  			characterSetClient, err := ctx.GetSessionVariable(ctx, "character_set_client")
   427  			if err != nil {
   428  				return nil, err
   429  			}
   430  			collationConnection, err := ctx.GetSessionVariable(ctx, "collation_connection")
   431  			if err != nil {
   432  				return nil, err
   433  			}
   434  			collationServer, err := ctx.GetSessionVariable(ctx, "collation_server")
   435  			if err != nil {
   436  				return nil, err
   437  			}
   438  			return sql.RowsToRowIter(sql.Row{
   439  				trigger.Name,            // Trigger
   440  				"",                      // sql_mode
   441  				trigger.CreateStatement, // SQL Original Statement
   442  				characterSetClient,      // character_set_client
   443  				collationConnection,     // collation_connection
   444  				collationServer,         // Database Collation
   445  				trigger.CreatedAt,       // Created
   446  			}), nil
   447  		}
   448  	}
   449  	return nil, sql.ErrTriggerDoesNotExist.New(n.TriggerName)
   450  }
   451  
   452  func (b *BaseBuilder) buildShowColumns(ctx *sql.Context, n *plan.ShowColumns, row sql.Row) (sql.RowIter, error) {
   453  	span, _ := ctx.Span("plan.ShowColumns")
   454  
   455  	schema := n.TargetSchema()
   456  	var rows = make([]sql.Row, len(schema))
   457  	for i, col := range schema {
   458  		var row sql.Row
   459  		var collation interface{}
   460  		if types.IsTextOnly(col.Type) {
   461  			collation = sql.Collation_Default.String()
   462  		}
   463  
   464  		var null = "NO"
   465  		if col.Nullable {
   466  			null = "YES"
   467  		}
   468  
   469  		node := n.Child
   470  		if exchange, ok := node.(*plan.Exchange); ok {
   471  			node = exchange.Child
   472  		}
   473  		key := ""
   474  		switch table := node.(type) {
   475  		case *plan.ResolvedTable:
   476  			if col.PrimaryKey {
   477  				key = "PRI"
   478  			} else if isFirstColInUniqueKey(n, col, table) {
   479  				key = "UNI"
   480  			} else if isFirstColInNonUniqueKey(n, col, table) {
   481  				key = "MUL"
   482  			}
   483  		case *plan.SubqueryAlias:
   484  			// no key info for views
   485  		default:
   486  			panic(fmt.Sprintf("unexpected type %T", n.Child))
   487  		}
   488  
   489  		var defaultVal string
   490  		if col.Default != nil {
   491  			defaultVal = col.Default.String()
   492  		} else {
   493  			// From: https://dev.mysql.com/doc/refman/8.0/en/show-columns.html
   494  			// The default value for the column. This is NULL if the column has an explicit default of NULL,
   495  			// or if the column definition includes no DEFAULT clause.
   496  			defaultVal = "NULL"
   497  		}
   498  
   499  		extra := col.Extra
   500  		// If extra is not defined, fill it here.
   501  		if extra == "" && !col.Default.IsLiteral() {
   502  			extra = "DEFAULT_GENERATED"
   503  		}
   504  
   505  		if n.Full {
   506  			row = sql.Row{
   507  				col.Name,
   508  				col.Type.String(),
   509  				collation,
   510  				null,
   511  				key,
   512  				defaultVal,
   513  				extra,
   514  				"", // Privileges
   515  				col.Comment,
   516  			}
   517  		} else {
   518  			row = sql.Row{
   519  				col.Name,
   520  				col.Type.String(),
   521  				null,
   522  				key,
   523  				defaultVal,
   524  				extra,
   525  			}
   526  		}
   527  
   528  		rows[i] = row
   529  	}
   530  
   531  	return sql.NewSpanIter(span, sql.RowsToRowIter(rows...)), nil
   532  }
   533  
   534  func (b *BaseBuilder) buildShowVariables(ctx *sql.Context, n *plan.ShowVariables, row sql.Row) (sql.RowIter, error) {
   535  	var rows []sql.Row
   536  	var sysVars map[string]interface{}
   537  
   538  	if n.Global {
   539  		sysVars = sql.SystemVariables.GetAllGlobalVariables()
   540  	} else {
   541  		sysVars = ctx.GetAllSessionVariables()
   542  	}
   543  
   544  	for k, v := range sysVars {
   545  		if n.Filter != nil {
   546  			res, err := n.Filter.Eval(ctx, sql.Row{k})
   547  			if err != nil {
   548  				return nil, err
   549  			}
   550  			res, _, err = types.Boolean.Convert(res)
   551  			if err != nil {
   552  				ctx.Warn(1292, err.Error())
   553  				continue
   554  			}
   555  			if res.(int8) == 0 {
   556  				continue
   557  			}
   558  		}
   559  		rows = append(rows, sql.NewRow(k, v))
   560  	}
   561  
   562  	sort.Slice(rows, func(i, j int) bool {
   563  		return rows[i][0].(string) < rows[j][0].(string)
   564  	})
   565  
   566  	return sql.RowsToRowIter(rows...), nil
   567  }
   568  
   569  func (b *BaseBuilder) buildShowTriggers(ctx *sql.Context, n *plan.ShowTriggers, row sql.Row) (sql.RowIter, error) {
   570  	var rows []sql.Row
   571  	for _, trigger := range n.Triggers {
   572  		triggerEvent := strings.ToUpper(trigger.TriggerEvent)
   573  		triggerTime := strings.ToUpper(trigger.TriggerTime)
   574  		tableName := trigger.Table.(sql.Nameable).Name()
   575  		characterSetClient, err := ctx.GetSessionVariable(ctx, "character_set_client")
   576  		if err != nil {
   577  			return nil, err
   578  		}
   579  		collationConnection, err := ctx.GetSessionVariable(ctx, "collation_connection")
   580  		if err != nil {
   581  			return nil, err
   582  		}
   583  		collationServer, err := ctx.GetSessionVariable(ctx, "collation_server")
   584  		if err != nil {
   585  			return nil, err
   586  		}
   587  		rows = append(rows, sql.Row{
   588  			trigger.TriggerName, // Trigger
   589  			triggerEvent,        // Event
   590  			tableName,           // Table
   591  			trigger.BodyString,  // Statement
   592  			triggerTime,         // Timing
   593  			trigger.CreatedAt,   // Created
   594  			"",                  // sql_mode
   595  			"",                  // Definer
   596  			characterSetClient,  // character_set_client
   597  			collationConnection, // collation_connection
   598  			collationServer,     // Database Collation
   599  		})
   600  	}
   601  	return sql.RowsToRowIter(rows...), nil
   602  }
   603  
   604  func (b *BaseBuilder) buildDescribe(ctx *sql.Context, n *plan.Describe, row sql.Row) (sql.RowIter, error) {
   605  	return &describeIter{schema: n.Child.Schema()}, nil
   606  }
   607  
   608  func (b *BaseBuilder) buildShowDatabases(ctx *sql.Context, n *plan.ShowDatabases, row sql.Row) (sql.RowIter, error) {
   609  	dbs := n.Catalog.AllDatabases(ctx)
   610  	var rows = make([]sql.Row, 0, len(dbs))
   611  	for _, db := range dbs {
   612  		rows = append(rows, sql.Row{db.Name()})
   613  	}
   614  	if _, err := n.Catalog.Database(ctx, "mysql"); err == nil {
   615  		rows = append(rows, sql.Row{"mysql"})
   616  	}
   617  
   618  	sort.Slice(rows, func(i, j int) bool {
   619  		return strings.Compare(rows[i][0].(string), rows[j][0].(string)) < 0
   620  	})
   621  
   622  	return sql.RowsToRowIter(rows...), nil
   623  }
   624  
   625  func (b *BaseBuilder) buildShowGrants(ctx *sql.Context, n *plan.ShowGrants, row sql.Row) (sql.RowIter, error) {
   626  	mysqlDb, ok := n.MySQLDb.(*mysql_db.MySQLDb)
   627  	if !ok {
   628  		return nil, sql.ErrDatabaseNotFound.New("mysql")
   629  	}
   630  	if n.For == nil || n.CurrentUser {
   631  		client := ctx.Session.Client()
   632  		n.For = &plan.UserName{
   633  			Name: client.User,
   634  			Host: client.Address,
   635  		}
   636  	}
   637  
   638  	reader := mysqlDb.Reader()
   639  	defer reader.Close()
   640  
   641  	user := mysqlDb.GetUser(reader, n.For.Name, n.For.Host, false)
   642  	if user == nil {
   643  		return nil, sql.ErrShowGrantsUserDoesNotExist.New(n.For.Name, n.For.Host)
   644  	}
   645  
   646  	//TODO: implement USING, perhaps by creating a new context with the chosen roles set as the active roles
   647  	var rows []sql.Row
   648  	userStr := user.UserHostToString("`")
   649  	privStr := generatePrivStrings("*", "*", userStr, user.PrivilegeSet.ToSlice())
   650  	rows = append(rows, sql.Row{privStr})
   651  
   652  	for _, db := range user.PrivilegeSet.GetDatabases() {
   653  		dbStr := fmt.Sprintf("`%s`", db.Name())
   654  		if privStr = generatePrivStrings(dbStr, "*", userStr, db.ToSlice()); len(privStr) != 0 {
   655  			rows = append(rows, sql.Row{privStr})
   656  		}
   657  
   658  		for _, tbl := range db.GetTables() {
   659  			tblStr := fmt.Sprintf("`%s`", tbl.Name())
   660  			privStr = generatePrivStrings(dbStr, tblStr, userStr, tbl.ToSlice())
   661  			rows = append(rows, sql.Row{privStr})
   662  		}
   663  
   664  		for _, routine := range db.GetRoutines() {
   665  			quotedRoutine := fmt.Sprintf("`%s`", routine.RoutineName())
   666  			privStr = generateRoutinePrivStrings(dbStr, quotedRoutine, routine.RoutineType(), userStr, routine.ToSlice())
   667  			rows = append(rows, sql.Row{privStr})
   668  		}
   669  
   670  		// TODO: display column privileges
   671  	}
   672  
   673  	sb := strings.Builder{}
   674  
   675  	roleEdges := reader.GetToUserRoleEdges(mysql_db.RoleEdgesToKey{
   676  		ToHost: user.Host,
   677  		ToUser: user.User,
   678  	})
   679  	for i, roleEdge := range roleEdges {
   680  		if i > 0 {
   681  			sb.WriteString(", ")
   682  		}
   683  		sb.WriteString(roleEdge.FromString("`"))
   684  	}
   685  	if sb.Len() > 0 {
   686  		rows = append(rows, sql.Row{fmt.Sprintf("GRANT %s TO %s", sb.String(), user.UserHostToString("`"))})
   687  	}
   688  
   689  	sb.Reset()
   690  	for i, dynamicPrivWithWgo := range user.PrivilegeSet.ToSliceDynamic(true) {
   691  		if i > 0 {
   692  			sb.WriteString(", ")
   693  		}
   694  		sb.WriteString(dynamicPrivWithWgo)
   695  	}
   696  	if sb.Len() > 0 {
   697  		rows = append(rows, sql.Row{fmt.Sprintf("GRANT %s ON *.* TO %s WITH GRANT OPTION", sb.String(), user.UserHostToString("`"))})
   698  	}
   699  	sb.Reset()
   700  	for i, dynamicPrivWithoutWgo := range user.PrivilegeSet.ToSliceDynamic(false) {
   701  		if i > 0 {
   702  			sb.WriteString(", ")
   703  		}
   704  		sb.WriteString(dynamicPrivWithoutWgo)
   705  	}
   706  	if sb.Len() > 0 {
   707  		rows = append(rows, sql.Row{fmt.Sprintf("GRANT %s ON *.* TO %s", sb.String(), user.UserHostToString("`"))})
   708  	}
   709  	return sql.RowsToRowIter(rows...), nil
   710  }
   711  
   712  func (b *BaseBuilder) buildShowIndexes(ctx *sql.Context, n *plan.ShowIndexes, row sql.Row) (sql.RowIter, error) {
   713  	table, ok := n.Child.(*plan.ResolvedTable)
   714  	if !ok {
   715  		panic(fmt.Sprintf("unexpected type %T", n.Child))
   716  	}
   717  
   718  	return &showIndexesIter{
   719  		table: table,
   720  		idxs:  newIndexesToShow(n.IndexesToShow),
   721  	}, nil
   722  }
   723  
   724  func (b *BaseBuilder) buildShowCreateTable(ctx *sql.Context, n *plan.ShowCreateTable, row sql.Row) (sql.RowIter, error) {
   725  	return &showCreateTablesIter{
   726  		table:    n.Child,
   727  		isView:   n.IsView,
   728  		indexes:  n.Indexes,
   729  		checks:   n.Checks(),
   730  		schema:   n.TargetSchema(),
   731  		pkSchema: n.PrimaryKeySchema,
   732  	}, nil
   733  }
   734  
   735  func (b *BaseBuilder) buildShowReplicaStatus(ctx *sql.Context, n *plan.ShowReplicaStatus, row sql.Row) (sql.RowIter, error) {
   736  	if n.ReplicaController == nil {
   737  		return sql.RowsToRowIter(), nil
   738  	}
   739  
   740  	status, err := n.ReplicaController.GetReplicaStatus(ctx)
   741  	if err != nil {
   742  		return nil, err
   743  	}
   744  	if status == nil {
   745  		return sql.RowsToRowIter(), nil
   746  	}
   747  
   748  	replicateDoTables := strings.Join(status.ReplicateDoTables, ",")
   749  	replicateIgnoreTables := strings.Join(status.ReplicateIgnoreTables, ",")
   750  
   751  	lastIoErrorTimestamp := formatReplicaStatusTimestamp(status.LastIoErrorTimestamp)
   752  	lastSqlErrorTimestamp := formatReplicaStatusTimestamp(status.LastSqlErrorTimestamp)
   753  
   754  	row = sql.Row{
   755  		"",                       // Replica_IO_State
   756  		status.SourceHost,        // Source_Host
   757  		status.SourceUser,        // Source_User
   758  		status.SourcePort,        // Source_Port
   759  		status.ConnectRetry,      // Connect_Retry
   760  		"INVALID",                // Source_Log_File
   761  		0,                        // Read_Source_Log_Pos
   762  		nil,                      // Relay_Log_File
   763  		nil,                      // Relay_Log_Pos
   764  		"INVALID",                // Relay_Source_Log_File
   765  		status.ReplicaIoRunning,  // Replica_IO_Running
   766  		status.ReplicaSqlRunning, // Replica_SQL_Running
   767  		nil,                      // Replicate_Do_DB
   768  		nil,                      // Replicate_Ignore_DB
   769  		replicateDoTables,        // Replicate_Do_Table
   770  		replicateIgnoreTables,    // Replicate_Ignore_Table
   771  		nil,                      // Replicate_Wild_Do_Table
   772  		nil,                      // Replicate_Wild_Ignore_Table
   773  		status.LastSqlErrNumber,  // Last_Errno
   774  		status.LastSqlError,      // Last_Error
   775  		nil,                      // Skip_Counter
   776  		0,                        // Exec_Source_Log_Pos
   777  		nil,                      // Relay_Log_Space
   778  		"None",                   // Until_Condition
   779  		nil,                      // Until_Log_File
   780  		nil,                      // Until_Log_Pos
   781  		"Ignored",                // Source_SSL_Allowed
   782  		nil,                      // Source_SSL_CA_File
   783  		nil,                      // Source_SSL_CA_Path
   784  		nil,                      // Source_SSL_Cert
   785  		nil,                      // Source_SSL_Cipher
   786  		nil,                      // Source_SSL_CRL_File
   787  		nil,                      // Source_SSL_CRL_Path
   788  		nil,                      // Source_SSL_Key
   789  		nil,                      // Source_SSL_Verify_Server_Cert
   790  		0,                        // Seconds_Behind_Source
   791  		status.LastIoErrNumber,   // Last_IO_Errno
   792  		status.LastIoError,       // Last_IO_Error
   793  		status.LastSqlErrNumber,  // Last_SQL_Errno
   794  		status.LastSqlError,      // Last_SQL_Error
   795  		nil,                      // Replicate_Ignore_Server_Ids
   796  		status.SourceServerId,    // Source_Server_Id
   797  		status.SourceServerUuid,  // Source_UUID
   798  		nil,                      // Source_Info_File
   799  		0,                        // SQL_Delay
   800  		0,                        // SQL_Remaining_Delay
   801  		nil,                      // Replica_SQL_Running_State
   802  		status.SourceRetryCount,  // Source_Retry_Count
   803  		nil,                      // Source_Bind
   804  		lastIoErrorTimestamp,     // Last_IO_Error_Timestamp
   805  		lastSqlErrorTimestamp,    // Last_SQL_Error_Timestamp
   806  		status.RetrievedGtidSet,  // Retrieved_Gtid_Set
   807  		status.ExecutedGtidSet,   // Executed_Gtid_Set
   808  		status.AutoPosition,      // Auto_Position
   809  		nil,                      // Replicate_Rewrite_DB
   810  	}
   811  
   812  	return sql.RowsToRowIter(row), nil
   813  }
   814  
   815  func (b *BaseBuilder) buildShowCreateEvent(ctx *sql.Context, n *plan.ShowCreateEvent, row sql.Row) (sql.RowIter, error) {
   816  	characterSetClient, err := ctx.GetSessionVariable(ctx, "character_set_client")
   817  	if err != nil {
   818  		return nil, err
   819  	}
   820  	collationConnection, err := ctx.GetSessionVariable(ctx, "collation_connection")
   821  	if err != nil {
   822  		return nil, err
   823  	}
   824  	collationServer, err := ctx.GetSessionVariable(ctx, "collation_server")
   825  	if err != nil {
   826  		return nil, err
   827  	}
   828  
   829  	// Convert the Event's timestamps into the session's timezone (they are always stored in UTC)
   830  	newEvent := n.Event.ConvertTimesFromUTCToTz(gmstime.SystemTimezoneOffset())
   831  	n.Event = *newEvent
   832  
   833  	// TODO: fill time_zone with appropriate values
   834  	return sql.RowsToRowIter(sql.Row{
   835  		n.Event.Name,                   // Event
   836  		n.Event.SqlMode,                // sql_mode
   837  		"SYSTEM",                       // time_zone
   838  		n.Event.CreateEventStatement(), // Create Event
   839  		characterSetClient,             // character_set_client
   840  		collationConnection,            // collation_connection
   841  		collationServer,                // Database Collation
   842  	}), nil
   843  }