vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/show.go (about)

     1  /*
     2  Copyright 2020 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package planbuilder
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"sort"
    23  	"strings"
    24  
    25  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    26  
    27  	"vitess.io/vitess/go/mysql/collations"
    28  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    29  
    30  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    31  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    32  
    33  	"vitess.io/vitess/go/sqltypes"
    34  	"vitess.io/vitess/go/vt/key"
    35  	querypb "vitess.io/vitess/go/vt/proto/query"
    36  	"vitess.io/vitess/go/vt/sqlparser"
    37  	"vitess.io/vitess/go/vt/vterrors"
    38  	"vitess.io/vitess/go/vt/vtgate/engine"
    39  )
    40  
    41  const (
    42  	utf8    = "utf8"
    43  	utf8mb4 = "utf8mb4"
    44  	both    = "both"
    45  	charset = "charset"
    46  )
    47  
    48  func buildShowPlan(sql string, stmt *sqlparser.Show, _ *sqlparser.ReservedVars, vschema plancontext.VSchema) (*planResult, error) {
    49  	if vschema.Destination() != nil {
    50  		return buildByPassDDLPlan(sql, vschema)
    51  	}
    52  
    53  	var prim engine.Primitive
    54  	var err error
    55  	switch show := stmt.Internal.(type) {
    56  	case *sqlparser.ShowBasic:
    57  		prim, err = buildShowBasicPlan(show, vschema)
    58  	case *sqlparser.ShowCreate:
    59  		prim, err = buildShowCreatePlan(show, vschema)
    60  	case *sqlparser.ShowOther:
    61  		prim, err = buildShowOtherPlan(sql, vschema)
    62  	default:
    63  		return nil, vterrors.VT13001(fmt.Sprintf("undefined SHOW type: %T", stmt.Internal))
    64  	}
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	return newPlanResult(prim), nil
    70  }
    71  
    72  func buildShowOtherPlan(sql string, vschema plancontext.VSchema) (engine.Primitive, error) {
    73  	ks, err := vschema.AnyKeyspace()
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	return &engine.Send{
    78  		Keyspace:          ks,
    79  		TargetDestination: key.DestinationAnyShard{},
    80  		Query:             sql,
    81  		SingleShardOnly:   true,
    82  	}, nil
    83  }
    84  
    85  func buildShowBasicPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
    86  	switch show.Command {
    87  	case sqlparser.Charset:
    88  		return buildCharsetPlan(show)
    89  	case sqlparser.Collation, sqlparser.Function, sqlparser.Privilege, sqlparser.Procedure:
    90  		return buildSendAnywherePlan(show, vschema)
    91  	case sqlparser.VariableGlobal, sqlparser.VariableSession:
    92  		return buildVariablePlan(show, vschema)
    93  	case sqlparser.Column, sqlparser.Index:
    94  		return buildShowTblPlan(show, vschema)
    95  	case sqlparser.Database, sqlparser.Keyspace:
    96  		return buildDBPlan(show, vschema)
    97  	case sqlparser.OpenTable, sqlparser.TableStatus, sqlparser.Table, sqlparser.Trigger:
    98  		return buildPlanWithDB(show, vschema)
    99  	case sqlparser.StatusGlobal, sqlparser.StatusSession:
   100  		return buildSendAnywherePlan(show, vschema)
   101  	case sqlparser.VitessMigrations:
   102  		return buildShowVMigrationsPlan(show, vschema)
   103  	case sqlparser.VGtidExecGlobal:
   104  		return buildShowVGtidPlan(show, vschema)
   105  	case sqlparser.GtidExecGlobal:
   106  		return buildShowGtidPlan(show, vschema)
   107  	case sqlparser.Warnings:
   108  		return buildWarnings()
   109  	case sqlparser.Plugins:
   110  		return buildPluginsPlan()
   111  	case sqlparser.Engines:
   112  		return buildEnginesPlan()
   113  	case sqlparser.VitessReplicationStatus, sqlparser.VitessShards, sqlparser.VitessTablets, sqlparser.VitessVariables:
   114  		return &engine.ShowExec{
   115  			Command:    show.Command,
   116  			ShowFilter: show.Filter,
   117  		}, nil
   118  	case sqlparser.VitessTarget:
   119  		return buildShowTargetPlan(vschema)
   120  	case sqlparser.VschemaTables:
   121  		return buildVschemaTablesPlan(vschema)
   122  	case sqlparser.VschemaVindexes:
   123  		return buildVschemaVindexesPlan(show, vschema)
   124  	}
   125  	return nil, vterrors.VT13001(fmt.Sprintf("unknown SHOW query type %s", show.Command.ToString()))
   126  
   127  }
   128  
   129  func buildShowTargetPlan(vschema plancontext.VSchema) (engine.Primitive, error) {
   130  	rows := [][]sqltypes.Value{buildVarCharRow(vschema.TargetString())}
   131  	return engine.NewRowsPrimitive(rows,
   132  		buildVarCharFields("Target")), nil
   133  }
   134  
   135  func buildCharsetPlan(show *sqlparser.ShowBasic) (engine.Primitive, error) {
   136  	fields := buildVarCharFields("Charset", "Description", "Default collation")
   137  	maxLenField := &querypb.Field{Name: "Maxlen", Type: sqltypes.Int32}
   138  	fields = append(fields, maxLenField)
   139  
   140  	charsets := []string{utf8, utf8mb4}
   141  	rows, err := generateCharsetRows(show.Filter, charsets)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	return engine.NewRowsPrimitive(rows, fields), nil
   147  }
   148  
   149  func buildSendAnywherePlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   150  	ks, err := vschema.AnyKeyspace()
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	return &engine.Send{
   155  		Keyspace:          ks,
   156  		TargetDestination: key.DestinationAnyShard{},
   157  		Query:             sqlparser.String(show),
   158  		IsDML:             false,
   159  		SingleShardOnly:   true,
   160  	}, nil
   161  }
   162  
   163  func buildVariablePlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   164  	plan, err := buildSendAnywherePlan(show, vschema)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	plan = engine.NewReplaceVariables(plan)
   169  	return plan, nil
   170  }
   171  
   172  func buildShowTblPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   173  	if !show.DbName.IsEmpty() {
   174  		show.Tbl.Qualifier = sqlparser.NewIdentifierCS(show.DbName.String())
   175  		// Remove Database Name from the query.
   176  		show.DbName = sqlparser.NewIdentifierCS("")
   177  	}
   178  
   179  	dest := key.Destination(key.DestinationAnyShard{})
   180  	var ks *vindexes.Keyspace
   181  	var err error
   182  
   183  	if !show.Tbl.Qualifier.IsEmpty() && sqlparser.SystemSchema(show.Tbl.Qualifier.String()) {
   184  		ks, err = vschema.AnyKeyspace()
   185  		if err != nil {
   186  			return nil, err
   187  		}
   188  	} else {
   189  		table, _, _, _, destination, err := vschema.FindTableOrVindex(show.Tbl)
   190  		if err != nil {
   191  			return nil, err
   192  		}
   193  		if table == nil {
   194  			return nil, vterrors.VT05004(show.Tbl.Name.String())
   195  		}
   196  		// Update the table.
   197  		show.Tbl.Qualifier = sqlparser.NewIdentifierCS("")
   198  		show.Tbl.Name = table.Name
   199  
   200  		if destination != nil {
   201  			dest = destination
   202  		}
   203  		ks = table.Keyspace
   204  	}
   205  
   206  	return &engine.Send{
   207  		Keyspace:          ks,
   208  		TargetDestination: dest,
   209  		Query:             sqlparser.String(show),
   210  		IsDML:             false,
   211  		SingleShardOnly:   true,
   212  	}, nil
   213  }
   214  
   215  func buildDBPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   216  	ks, err := vschema.AllKeyspace()
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	var filter *regexp.Regexp
   222  
   223  	if show.Filter != nil {
   224  		filter = sqlparser.LikeToRegexp(show.Filter.Like)
   225  	}
   226  
   227  	if filter == nil {
   228  		filter = regexp.MustCompile(".*")
   229  	}
   230  
   231  	// rows := make([][]sqltypes.Value, 0, len(ks)+4)
   232  	var rows [][]sqltypes.Value
   233  
   234  	if show.Command == sqlparser.Database {
   235  		// Hard code default databases
   236  		ks = append(ks, &vindexes.Keyspace{Name: "information_schema"},
   237  			&vindexes.Keyspace{Name: "mysql"},
   238  			&vindexes.Keyspace{Name: "sys"},
   239  			&vindexes.Keyspace{Name: "performance_schema"})
   240  	}
   241  
   242  	for _, v := range ks {
   243  		if filter.MatchString(v.Name) {
   244  			rows = append(rows, buildVarCharRow(v.Name))
   245  		}
   246  	}
   247  	return engine.NewRowsPrimitive(rows, buildVarCharFields("Database")), nil
   248  }
   249  
   250  // buildShowVMigrationsPlan serves `SHOW VITESS_MIGRATIONS ...` queries. It invokes queries on _vt.schema_migrations on all PRIMARY tablets on keyspace's shards.
   251  func buildShowVMigrationsPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   252  	dest, ks, tabletType, err := vschema.TargetDestination(show.DbName.String())
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	if ks == nil {
   257  		return nil, vterrors.VT09005()
   258  	}
   259  
   260  	if tabletType != topodatapb.TabletType_PRIMARY {
   261  		return nil, vterrors.VT09006("SHOW")
   262  	}
   263  
   264  	if dest == nil {
   265  		dest = key.DestinationAllShards{}
   266  	}
   267  
   268  	sql := "SELECT * FROM _vt.schema_migrations"
   269  
   270  	if show.Filter != nil {
   271  		if show.Filter.Filter != nil {
   272  			sql += fmt.Sprintf(" where %s", sqlparser.String(show.Filter.Filter))
   273  		} else if show.Filter.Like != "" {
   274  			lit := sqlparser.String(sqlparser.NewStrLiteral(show.Filter.Like))
   275  			sql += fmt.Sprintf(" where migration_uuid LIKE %s OR migration_context LIKE %s OR migration_status LIKE %s", lit, lit, lit)
   276  		}
   277  	}
   278  	return &engine.Send{
   279  		Keyspace:          ks,
   280  		TargetDestination: dest,
   281  		Query:             sql,
   282  	}, nil
   283  }
   284  
   285  func buildPlanWithDB(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   286  	dbName := show.DbName
   287  	dbDestination := show.DbName.String()
   288  	if sqlparser.SystemSchema(dbDestination) {
   289  		ks, err := vschema.AnyKeyspace()
   290  		if err != nil {
   291  			return nil, err
   292  		}
   293  		dbDestination = ks.Name
   294  	} else {
   295  		// Remove Database Name from the query.
   296  		show.DbName = sqlparser.NewIdentifierCS("")
   297  	}
   298  	destination, keyspace, _, err := vschema.TargetDestination(dbDestination)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  	if destination == nil {
   303  		destination = key.DestinationAnyShard{}
   304  	}
   305  
   306  	if dbName.IsEmpty() {
   307  		dbName = sqlparser.NewIdentifierCS(keyspace.Name)
   308  	}
   309  
   310  	query := sqlparser.String(show)
   311  	var plan engine.Primitive
   312  	plan = &engine.Send{
   313  		Keyspace:          keyspace,
   314  		TargetDestination: destination,
   315  		Query:             query,
   316  		IsDML:             false,
   317  		SingleShardOnly:   true,
   318  	}
   319  	if show.Command == sqlparser.Table {
   320  		plan, err = engine.NewRenameField([]string{"Tables_in_" + dbName.String()}, []int{0}, plan)
   321  		if err != nil {
   322  			return nil, err
   323  		}
   324  	}
   325  	return plan, nil
   326  
   327  }
   328  
   329  func buildVarCharFields(names ...string) []*querypb.Field {
   330  	fields := make([]*querypb.Field, len(names))
   331  	for i, v := range names {
   332  		fields[i] = &querypb.Field{
   333  			Name:    v,
   334  			Type:    sqltypes.VarChar,
   335  			Charset: collations.CollationUtf8ID,
   336  			Flags:   uint32(querypb.MySqlFlag_NOT_NULL_FLAG),
   337  		}
   338  	}
   339  	return fields
   340  }
   341  
   342  func buildVarCharRow(values ...string) []sqltypes.Value {
   343  	row := make([]sqltypes.Value, len(values))
   344  	for i, v := range values {
   345  		row[i] = sqltypes.NewVarChar(v)
   346  	}
   347  	return row
   348  }
   349  
   350  func generateCharsetRows(showFilter *sqlparser.ShowFilter, colNames []string) ([][]sqltypes.Value, error) {
   351  	if showFilter == nil {
   352  		return buildCharsetRows(both), nil
   353  	}
   354  
   355  	var filteredColName string
   356  	var err error
   357  
   358  	if showFilter.Like != "" {
   359  		filteredColName, err = checkLikeOpt(showFilter.Like, colNames)
   360  		if err != nil {
   361  			return nil, err
   362  		}
   363  
   364  	} else {
   365  		cmpExp, ok := showFilter.Filter.(*sqlparser.ComparisonExpr)
   366  		if !ok {
   367  			return nil, vterrors.VT12001("expect a 'LIKE' or '=' expression")
   368  		}
   369  
   370  		left, ok := cmpExp.Left.(*sqlparser.ColName)
   371  		if !ok {
   372  			return nil, vterrors.VT12001("expect left side to be 'charset'")
   373  		}
   374  		leftOk := left.Name.EqualString(charset)
   375  
   376  		if leftOk {
   377  			literal, ok := cmpExp.Right.(*sqlparser.Literal)
   378  			if !ok {
   379  				return nil, vterrors.VT12001("we expect the right side to be a string")
   380  			}
   381  			rightString := literal.Val
   382  
   383  			switch cmpExp.Operator {
   384  			case sqlparser.EqualOp:
   385  				for _, colName := range colNames {
   386  					if rightString == colName {
   387  						filteredColName = colName
   388  					}
   389  				}
   390  			case sqlparser.LikeOp:
   391  				filteredColName, err = checkLikeOpt(rightString, colNames)
   392  				if err != nil {
   393  					return nil, err
   394  				}
   395  			}
   396  		}
   397  
   398  	}
   399  
   400  	return buildCharsetRows(filteredColName), nil
   401  }
   402  
   403  func buildCharsetRows(colName string) [][]sqltypes.Value {
   404  	row0 := buildVarCharRow(
   405  		"utf8",
   406  		"UTF-8 Unicode",
   407  		"utf8_general_ci")
   408  	row0 = append(row0, sqltypes.NewInt32(3))
   409  	row1 := buildVarCharRow(
   410  		"utf8mb4",
   411  		"UTF-8 Unicode",
   412  		"utf8mb4_general_ci")
   413  	row1 = append(row1, sqltypes.NewInt32(4))
   414  
   415  	switch colName {
   416  	case utf8:
   417  		return [][]sqltypes.Value{row0}
   418  	case utf8mb4:
   419  		return [][]sqltypes.Value{row1}
   420  	case both:
   421  		return [][]sqltypes.Value{row0, row1}
   422  	}
   423  
   424  	return [][]sqltypes.Value{}
   425  }
   426  
   427  func checkLikeOpt(likeOpt string, colNames []string) (string, error) {
   428  	likeRegexp := strings.ReplaceAll(likeOpt, "%", ".*")
   429  	for _, v := range colNames {
   430  		match, err := regexp.MatchString(likeRegexp, v)
   431  		if err != nil {
   432  			return "", err
   433  		}
   434  		if match {
   435  			return v, nil
   436  		}
   437  	}
   438  
   439  	return "", nil
   440  }
   441  
   442  func buildShowCreatePlan(show *sqlparser.ShowCreate, vschema plancontext.VSchema) (engine.Primitive, error) {
   443  	switch show.Command {
   444  	case sqlparser.CreateDb:
   445  		return buildCreateDbPlan(show, vschema)
   446  	case sqlparser.CreateE, sqlparser.CreateF, sqlparser.CreateProc, sqlparser.CreateTr, sqlparser.CreateV:
   447  		return buildCreatePlan(show, vschema)
   448  	case sqlparser.CreateTbl:
   449  		return buildCreateTblPlan(show, vschema)
   450  	}
   451  	return nil, vterrors.VT13001("unknown SHOW query type %s", show.Command.ToString())
   452  }
   453  
   454  func buildCreateDbPlan(show *sqlparser.ShowCreate, vschema plancontext.VSchema) (engine.Primitive, error) {
   455  	dbName := show.Op.Name.String()
   456  	if sqlparser.SystemSchema(dbName) {
   457  		ks, err := vschema.AnyKeyspace()
   458  		if err != nil {
   459  			return nil, err
   460  		}
   461  		dbName = ks.Name
   462  	}
   463  
   464  	dest, ks, _, err := vschema.TargetDestination(dbName)
   465  	if err != nil {
   466  		return nil, err
   467  	}
   468  
   469  	if dest == nil {
   470  		dest = key.DestinationAnyShard{}
   471  	}
   472  
   473  	return &engine.Send{
   474  		Keyspace:          ks,
   475  		TargetDestination: dest,
   476  		Query:             sqlparser.String(show),
   477  		IsDML:             false,
   478  		SingleShardOnly:   true,
   479  	}, nil
   480  }
   481  
   482  func buildCreateTblPlan(show *sqlparser.ShowCreate, vschema plancontext.VSchema) (engine.Primitive, error) {
   483  	dest := key.Destination(key.DestinationAnyShard{})
   484  	var ks *vindexes.Keyspace
   485  	var err error
   486  
   487  	if !show.Op.Qualifier.IsEmpty() && sqlparser.SystemSchema(show.Op.Qualifier.String()) {
   488  		ks, err = vschema.AnyKeyspace()
   489  		if err != nil {
   490  			return nil, err
   491  		}
   492  	} else {
   493  		tbl, _, _, _, destKs, err := vschema.FindTableOrVindex(show.Op)
   494  		if err != nil {
   495  			return nil, err
   496  		}
   497  		if tbl == nil {
   498  			return nil, vterrors.VT05004(sqlparser.String(show.Op))
   499  		}
   500  		ks = tbl.Keyspace
   501  		if destKs != nil {
   502  			dest = destKs
   503  		}
   504  		show.Op.Qualifier = sqlparser.NewIdentifierCS("")
   505  		show.Op.Name = tbl.Name
   506  	}
   507  
   508  	return &engine.Send{
   509  		Keyspace:          ks,
   510  		TargetDestination: dest,
   511  		Query:             sqlparser.String(show),
   512  		IsDML:             false,
   513  		SingleShardOnly:   true,
   514  	}, nil
   515  
   516  }
   517  
   518  func buildCreatePlan(show *sqlparser.ShowCreate, vschema plancontext.VSchema) (engine.Primitive, error) {
   519  	dbName := ""
   520  	if !show.Op.Qualifier.IsEmpty() {
   521  		dbName = show.Op.Qualifier.String()
   522  	}
   523  
   524  	if sqlparser.SystemSchema(dbName) {
   525  		ks, err := vschema.AnyKeyspace()
   526  		if err != nil {
   527  			return nil, err
   528  		}
   529  		dbName = ks.Name
   530  	} else {
   531  		show.Op.Qualifier = sqlparser.NewIdentifierCS("")
   532  	}
   533  
   534  	dest, ks, _, err := vschema.TargetDestination(dbName)
   535  	if err != nil {
   536  		return nil, err
   537  	}
   538  	if dest == nil {
   539  		dest = key.DestinationAnyShard{}
   540  	}
   541  
   542  	return &engine.Send{
   543  		Keyspace:          ks,
   544  		TargetDestination: dest,
   545  		Query:             sqlparser.String(show),
   546  		IsDML:             false,
   547  		SingleShardOnly:   true,
   548  	}, nil
   549  
   550  }
   551  
   552  func buildShowVGtidPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   553  	send, err := buildShowGtidPlan(show, vschema)
   554  	if err != nil {
   555  		return nil, err
   556  	}
   557  	return &engine.OrderedAggregate{
   558  		PreProcess: true,
   559  		Aggregates: []*engine.AggregateParams{
   560  			{
   561  				Opcode: engine.AggregateGtid,
   562  				Col:    1,
   563  				Alias:  "global vgtid_executed",
   564  			},
   565  		},
   566  		TruncateColumnCount: 2,
   567  		Input:               send,
   568  	}, nil
   569  }
   570  
   571  func buildShowGtidPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   572  	dbName := ""
   573  	if !show.DbName.IsEmpty() {
   574  		dbName = show.DbName.String()
   575  	}
   576  	dest, ks, _, err := vschema.TargetDestination(dbName)
   577  	if err != nil {
   578  		return nil, err
   579  	}
   580  	if dest == nil {
   581  		dest = key.DestinationAllShards{}
   582  	}
   583  
   584  	return &engine.Send{
   585  		Keyspace:          ks,
   586  		TargetDestination: dest,
   587  		Query:             fmt.Sprintf(`select '%s' as db_name, @@global.gtid_executed as gtid_executed, :%s as shard`, ks.Name, engine.ShardName),
   588  		ShardNameNeeded:   true,
   589  	}, nil
   590  }
   591  
   592  func buildWarnings() (engine.Primitive, error) {
   593  
   594  	f := func(sa engine.SessionActions) (*sqltypes.Result, error) {
   595  		fields := []*querypb.Field{
   596  			{Name: "Level", Type: sqltypes.VarChar},
   597  			{Name: "Code", Type: sqltypes.Uint16},
   598  			{Name: "Message", Type: sqltypes.VarChar},
   599  		}
   600  
   601  		warns := sa.GetWarnings()
   602  		rows := make([][]sqltypes.Value, 0, len(warns))
   603  
   604  		for _, warn := range warns {
   605  			rows = append(rows, []sqltypes.Value{
   606  				sqltypes.NewVarChar("Warning"),
   607  				sqltypes.NewUint32(warn.Code),
   608  				sqltypes.NewVarChar(warn.Message),
   609  			})
   610  		}
   611  		return &sqltypes.Result{
   612  			Fields: fields,
   613  			Rows:   rows,
   614  		}, nil
   615  	}
   616  
   617  	return engine.NewSessionPrimitive("SHOW WARNINGS", f), nil
   618  }
   619  
   620  func buildPluginsPlan() (engine.Primitive, error) {
   621  	var rows [][]sqltypes.Value
   622  	rows = append(rows, buildVarCharRow(
   623  		"InnoDB",
   624  		"ACTIVE",
   625  		"STORAGE ENGINE",
   626  		"NULL",
   627  		"GPL"))
   628  
   629  	return engine.NewRowsPrimitive(rows,
   630  		buildVarCharFields("Name", "Status", "Type", "Library", "License")), nil
   631  }
   632  
   633  func buildEnginesPlan() (engine.Primitive, error) {
   634  	var rows [][]sqltypes.Value
   635  	rows = append(rows, buildVarCharRow(
   636  		"InnoDB",
   637  		"DEFAULT",
   638  		"Supports transactions, row-level locking, and foreign keys",
   639  		"YES",
   640  		"YES",
   641  		"YES"))
   642  
   643  	return engine.NewRowsPrimitive(rows,
   644  		buildVarCharFields("Engine", "Support", "Comment", "Transactions", "XA", "Savepoints")), nil
   645  }
   646  
   647  func buildVschemaTablesPlan(vschema plancontext.VSchema) (engine.Primitive, error) {
   648  	vs := vschema.GetVSchema()
   649  	ks, err := vschema.DefaultKeyspace()
   650  	if err != nil {
   651  		return nil, err
   652  	}
   653  	schemaKs, ok := vs.Keyspaces[ks.Name]
   654  	if !ok {
   655  		return nil, vterrors.VT05003(ks.Name)
   656  	}
   657  
   658  	var tables []string
   659  	for name := range schemaKs.Tables {
   660  		tables = append(tables, name)
   661  	}
   662  	sort.Strings(tables)
   663  
   664  	rows := make([][]sqltypes.Value, len(tables))
   665  	for i, v := range tables {
   666  		rows[i] = buildVarCharRow(v)
   667  	}
   668  
   669  	return engine.NewRowsPrimitive(rows, buildVarCharFields("Tables")), nil
   670  }
   671  
   672  func buildVschemaVindexesPlan(show *sqlparser.ShowBasic, vschema plancontext.VSchema) (engine.Primitive, error) {
   673  	vs := vschema.GetSrvVschema()
   674  	rows := make([][]sqltypes.Value, 0, 16)
   675  
   676  	if !show.Tbl.IsEmpty() {
   677  		_, ks, _, err := vschema.TargetDestination(show.Tbl.Qualifier.String())
   678  		if err != nil {
   679  			return nil, err
   680  		}
   681  		var schemaKs *vschemapb.Keyspace
   682  		var tbl *vschemapb.Table
   683  		if !ks.Sharded {
   684  			tbl = &vschemapb.Table{}
   685  		} else {
   686  			schemaKs = vs.Keyspaces[ks.Name]
   687  			tableName := show.Tbl.Name.String()
   688  			schemaTbl, ok := schemaKs.Tables[tableName]
   689  			if !ok {
   690  				return nil, vterrors.VT05005(tableName, ks.Name)
   691  			}
   692  			tbl = schemaTbl
   693  		}
   694  
   695  		for _, colVindex := range tbl.ColumnVindexes {
   696  			vindex, ok := schemaKs.Vindexes[colVindex.GetName()]
   697  			columns := colVindex.GetColumns()
   698  			if len(columns) == 0 {
   699  				columns = []string{colVindex.GetColumn()}
   700  			}
   701  			if ok {
   702  				params := make([]string, 0, 4)
   703  				for k, v := range vindex.GetParams() {
   704  					params = append(params, fmt.Sprintf("%s=%s", k, v))
   705  				}
   706  				sort.Strings(params)
   707  				rows = append(rows, buildVarCharRow(strings.Join(columns, ", "), colVindex.GetName(), vindex.GetType(), strings.Join(params, "; "), vindex.GetOwner()))
   708  			} else {
   709  				rows = append(rows, buildVarCharRow(strings.Join(columns, ", "), colVindex.GetName(), "", "", ""))
   710  			}
   711  		}
   712  
   713  		return engine.NewRowsPrimitive(rows,
   714  			buildVarCharFields("Columns", "Name", "Type", "Params", "Owner"),
   715  		), nil
   716  	}
   717  
   718  	// For the query interface to be stable we need to sort
   719  	// for each of the map iterations
   720  	ksNames := make([]string, 0, len(vs.Keyspaces))
   721  	for name := range vs.Keyspaces {
   722  		ksNames = append(ksNames, name)
   723  	}
   724  	sort.Strings(ksNames)
   725  	for _, ksName := range ksNames {
   726  		ks := vs.Keyspaces[ksName]
   727  
   728  		vindexNames := make([]string, 0, len(ks.Vindexes))
   729  		for name := range ks.Vindexes {
   730  			vindexNames = append(vindexNames, name)
   731  		}
   732  		sort.Strings(vindexNames)
   733  		for _, vindexName := range vindexNames {
   734  			vindex := ks.Vindexes[vindexName]
   735  
   736  			params := make([]string, 0, 4)
   737  			for k, v := range vindex.GetParams() {
   738  				params = append(params, fmt.Sprintf("%s=%s", k, v))
   739  			}
   740  			sort.Strings(params)
   741  			rows = append(rows, buildVarCharRow(ksName, vindexName, vindex.GetType(), strings.Join(params, "; "), vindex.GetOwner()))
   742  		}
   743  	}
   744  	return engine.NewRowsPrimitive(rows,
   745  		buildVarCharFields("Keyspace", "Name", "Type", "Params", "Owner"),
   746  	), nil
   747  
   748  }