github.com/dolthub/go-mysql-server@v0.18.0/sql/planbuilder/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 planbuilder
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/dolthub/vitess/go/sqltypes"
    23  	ast "github.com/dolthub/vitess/go/vt/sqlparser"
    24  
    25  	"github.com/dolthub/go-mysql-server/sql"
    26  	"github.com/dolthub/go-mysql-server/sql/binlogreplication"
    27  	"github.com/dolthub/go-mysql-server/sql/expression"
    28  	"github.com/dolthub/go-mysql-server/sql/plan"
    29  	"github.com/dolthub/go-mysql-server/sql/transform"
    30  	"github.com/dolthub/go-mysql-server/sql/types"
    31  )
    32  
    33  func (b *Builder) buildShow(inScope *scope, s *ast.Show) (outScope *scope) {
    34  	showType := strings.ToLower(s.Type)
    35  	switch showType {
    36  	case "processlist":
    37  		outScope = inScope.push()
    38  		outScope.node = plan.NewShowProcessList()
    39  	case ast.CreateTableStr, "create view":
    40  		return b.buildShowTable(inScope, s, showType)
    41  	case "create database", "create schema":
    42  		return b.buildShowDatabase(inScope, s)
    43  	case ast.CreateTriggerStr:
    44  		return b.buildShowTrigger(inScope, s)
    45  	case ast.CreateProcedureStr:
    46  		return b.buildShowProcedure(inScope, s)
    47  	case ast.CreateEventStr:
    48  		return b.buildShowEvent(inScope, s)
    49  	case "triggers":
    50  		return b.buildShowAllTriggers(inScope, s)
    51  	case "events":
    52  		return b.buildShowAllEvents(inScope, s)
    53  	case ast.ProcedureStatusStr:
    54  		return b.buildShowProcedureStatus(inScope, s)
    55  	case ast.FunctionStatusStr:
    56  		return b.buildShowFunctionStatus(inScope, s)
    57  	case ast.TableStatusStr:
    58  		return b.buildShowTableStatus(inScope, s)
    59  	case "index":
    60  		return b.buildShowIndex(inScope, s)
    61  	case ast.KeywordString(ast.VARIABLES):
    62  		return b.buildShowVariables(inScope, s)
    63  	case ast.KeywordString(ast.TABLES):
    64  		return b.buildShowAllTables(inScope, s)
    65  	case ast.KeywordString(ast.DATABASES), ast.KeywordString(ast.SCHEMAS):
    66  		return b.buildShowAllDatabases(inScope, s)
    67  	case ast.KeywordString(ast.FIELDS), ast.KeywordString(ast.COLUMNS):
    68  		return b.buildShowAllColumns(inScope, s)
    69  	case ast.KeywordString(ast.WARNINGS):
    70  		return b.buildShowWarnings(inScope, s)
    71  	case ast.KeywordString(ast.COLLATION):
    72  		return b.buildShowCollation(inScope, s)
    73  	case ast.KeywordString(ast.CHARSET):
    74  		return b.buildShowCharset(inScope, s)
    75  	case ast.KeywordString(ast.ENGINES):
    76  		return b.buildShowEngines(inScope, s)
    77  	case ast.KeywordString(ast.STATUS):
    78  		return b.buildShowStatus(inScope, s)
    79  	case ast.KeywordString(ast.PLUGINS):
    80  		return b.buildShowPlugins(inScope, s)
    81  	case "replica status":
    82  		outScope = inScope.push()
    83  		showRep := plan.NewShowReplicaStatus()
    84  		if binCat, ok := b.cat.(binlogreplication.BinlogReplicaCatalog); ok && binCat.IsBinlogReplicaCatalog() {
    85  			showRep.ReplicaController = binCat.GetBinlogReplicaController()
    86  		}
    87  		outScope.node = showRep
    88  	default:
    89  		unsupportedShow := fmt.Sprintf("SHOW %s", s.Type)
    90  		b.handleErr(sql.ErrUnsupportedFeature.New(unsupportedShow))
    91  	}
    92  	return
    93  }
    94  
    95  func (b *Builder) buildShowTable(inScope *scope, s *ast.Show, showType string) (outScope *scope) {
    96  	outScope = inScope.push()
    97  	var asOf *ast.AsOf
    98  	var asOfExpr sql.Expression
    99  	if s.ShowTablesOpt != nil && s.ShowTablesOpt.AsOf != nil {
   100  		asOfExpr = b.buildAsOfExpr(inScope, s.ShowTablesOpt.AsOf)
   101  		asOf = &ast.AsOf{Time: s.ShowTablesOpt.AsOf}
   102  	}
   103  
   104  	db := s.Database
   105  	if db == "" {
   106  		db = s.Table.Qualifier.String()
   107  	}
   108  	if db == "" {
   109  		db = b.currentDb().Name()
   110  	}
   111  
   112  	tableName := strings.ToLower(s.Table.Name.String())
   113  	tableScope, ok := b.buildResolvedTable(inScope, db, tableName, asOf)
   114  	if !ok {
   115  		err := sql.ErrTableNotFound.New(tableName)
   116  		b.handleErr(err)
   117  	}
   118  	rt, _ := tableScope.node.(*plan.ResolvedTable)
   119  	for _, c := range tableScope.node.Schema() {
   120  		outScope.newColumn(scopeColumn{
   121  			db:       c.DatabaseSource,
   122  			table:    c.Source,
   123  			col:      strings.ToLower(c.Name),
   124  			typ:      c.Type,
   125  			nullable: c.Nullable,
   126  		})
   127  	}
   128  
   129  	showCreate := plan.NewShowCreateTableWithAsOf(tableScope.node, showType == "create view", asOfExpr)
   130  	outScope.node = showCreate
   131  
   132  	if rt != nil {
   133  		checks := b.loadChecksFromTable(outScope, rt.Table)
   134  		// To match MySQL output format, transform the column names and wrap with backticks
   135  		for i, check := range checks {
   136  			checks[i].Expr, _, _ = transform.Expr(check.Expr, func(e sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
   137  				if t, ok := e.(*expression.GetField); ok {
   138  					return expression.NewUnresolvedColumn(fmt.Sprintf("`%s`", t.Name())), transform.NewTree, nil
   139  				}
   140  				return e, transform.SameTree, nil
   141  			})
   142  		}
   143  		showCreate = showCreate.WithChecks(checks).(*plan.ShowCreateTable)
   144  
   145  		showCreate.Indexes = b.getInfoSchemaIndexes(rt)
   146  
   147  		pks, _ := rt.Table.(sql.PrimaryKeyTable)
   148  		if pks != nil {
   149  			showCreate.PrimaryKeySchema = pks.PrimaryKeySchema()
   150  		}
   151  		outScope.node = b.modifySchemaTarget(outScope, showCreate, rt)
   152  
   153  	}
   154  	return
   155  }
   156  
   157  func (b *Builder) buildShowDatabase(inScope *scope, s *ast.Show) (outScope *scope) {
   158  	outScope = inScope.push()
   159  	dbName := s.Database
   160  	if dbName == "" {
   161  		dbName = b.ctx.GetCurrentDatabase()
   162  	}
   163  	db := b.resolveDb(dbName)
   164  	outScope.node = plan.NewShowCreateDatabase(
   165  		db,
   166  		s.IfNotExists,
   167  	)
   168  	return
   169  }
   170  
   171  func (b *Builder) buildShowTrigger(inScope *scope, s *ast.Show) (outScope *scope) {
   172  	outScope = inScope.push()
   173  	dbName := s.Table.Qualifier.String()
   174  	if dbName == "" {
   175  		dbName = b.ctx.GetCurrentDatabase()
   176  	}
   177  	db, err := b.cat.Database(b.ctx, dbName)
   178  	if err != nil {
   179  		b.handleErr(err)
   180  	}
   181  	outScope.node = plan.NewShowCreateTrigger(db, s.Table.Name.String())
   182  	return
   183  }
   184  
   185  func (b *Builder) buildShowAllTriggers(inScope *scope, s *ast.Show) (outScope *scope) {
   186  	dbName := s.Table.Qualifier.String()
   187  	if dbName == "" {
   188  		dbName = b.ctx.GetCurrentDatabase()
   189  	}
   190  	if dbName == "" && &s.ShowTablesOpt != nil {
   191  		dbName = s.ShowTablesOpt.DbName
   192  	}
   193  	db := b.resolveDb(dbName)
   194  
   195  	var node sql.Node = plan.NewShowTriggers(db)
   196  
   197  	outScope = inScope.push()
   198  	for _, c := range node.Schema() {
   199  		outScope.newColumn(scopeColumn{
   200  			db:       c.DatabaseSource,
   201  			table:    c.Source,
   202  			col:      strings.ToLower(c.Name),
   203  			typ:      c.Type,
   204  			nullable: c.Nullable,
   205  		})
   206  	}
   207  	var filter sql.Expression
   208  	if s.ShowTablesOpt != nil {
   209  		dbName = s.ShowTablesOpt.DbName
   210  		if s.ShowTablesOpt.Filter != nil {
   211  			if s.ShowTablesOpt.Filter.Filter != nil {
   212  				filter = b.buildScalar(outScope, s.ShowTablesOpt.Filter.Filter)
   213  			} else if s.ShowTablesOpt.Filter.Like != "" {
   214  				filter = expression.NewLike(
   215  					expression.NewGetField(2, types.LongText, "Table", false),
   216  					expression.NewLiteral(s.ShowTablesOpt.Filter.Like, types.LongText),
   217  					nil,
   218  				)
   219  			}
   220  		}
   221  	}
   222  
   223  	if filter != nil {
   224  		node = plan.NewFilter(filter, node)
   225  	}
   226  
   227  	outScope.node = node
   228  	return
   229  }
   230  
   231  func (b *Builder) buildShowEvent(inScope *scope, s *ast.Show) (outScope *scope) {
   232  	outScope = inScope.push()
   233  	dbName := strings.ToLower(s.Table.Qualifier.String())
   234  	if dbName == "" {
   235  		dbName = b.ctx.GetCurrentDatabase()
   236  	}
   237  
   238  	db := b.resolveDb(dbName)
   239  
   240  	eventName := strings.ToLower(s.Table.Name.String())
   241  	eventDb, ok := db.(sql.EventDatabase)
   242  	if !ok {
   243  		err := sql.ErrEventsNotSupported.New(db.Name())
   244  		b.handleErr(err)
   245  	}
   246  
   247  	event, exists, err := eventDb.GetEvent(b.ctx, eventName)
   248  	if err != nil {
   249  		b.handleErr(err)
   250  	}
   251  	if !exists {
   252  		err := sql.ErrUnknownEvent.New(eventName)
   253  		b.handleErr(err)
   254  	}
   255  
   256  	outScope.node = plan.NewShowCreateEvent(db, event)
   257  	return
   258  }
   259  
   260  func (b *Builder) buildShowAllEvents(inScope *scope, s *ast.Show) (outScope *scope) {
   261  	outScope = inScope.push()
   262  	var dbName string
   263  
   264  	if dbName == "" {
   265  		dbName = b.ctx.GetCurrentDatabase()
   266  	}
   267  	db := b.resolveDb(dbName)
   268  	showEvents := plan.NewShowEvents(db)
   269  	showEvents.Events = b.loadAllEventDefinitions(db)
   270  
   271  	var node sql.Node = showEvents
   272  	for _, c := range node.Schema() {
   273  		outScope.newColumn(scopeColumn{
   274  			db:    c.DatabaseSource,
   275  			table: c.Source,
   276  			col:   c.Name, typ: c.Type, nullable: c.Nullable})
   277  	}
   278  	var filter sql.Expression
   279  	if s.ShowTablesOpt != nil {
   280  		dbName = s.ShowTablesOpt.DbName
   281  		if s.ShowTablesOpt.Filter != nil {
   282  			if s.ShowTablesOpt.Filter.Filter != nil {
   283  				filter = b.buildScalar(outScope, s.ShowTablesOpt.Filter.Filter)
   284  			} else if s.ShowTablesOpt.Filter.Like != "" {
   285  				filter = expression.NewLike(
   286  					expression.NewGetField(1, types.LongText, "name", false),
   287  					expression.NewLiteral(s.ShowTablesOpt.Filter.Like, types.LongText),
   288  					nil,
   289  				)
   290  			}
   291  		}
   292  	}
   293  	if filter != nil {
   294  		node = plan.NewFilter(filter, node)
   295  	}
   296  
   297  	outScope.node = node
   298  	return
   299  }
   300  
   301  func (b *Builder) loadAllEventDefinitions(db sql.Database) []sql.EventDefinition {
   302  	if eventDb, ok := db.(sql.EventDatabase); ok {
   303  		events, _, err := eventDb.GetEvents(b.ctx)
   304  		if err != nil {
   305  			b.handleErr(err)
   306  		}
   307  		return events
   308  	}
   309  	return nil
   310  }
   311  
   312  func (b *Builder) buildShowProcedure(inScope *scope, s *ast.Show) (outScope *scope) {
   313  	outScope = inScope.push()
   314  	var db sql.Database
   315  	dbName := s.Table.Qualifier.String()
   316  	if dbName != "" {
   317  		db = b.resolveDb(dbName)
   318  	} else {
   319  		db = b.currentDb()
   320  	}
   321  	outScope.node = plan.NewShowCreateProcedure(db, s.Table.Name.String())
   322  	return
   323  }
   324  
   325  func (b *Builder) buildShowProcedureStatus(inScope *scope, s *ast.Show) (outScope *scope) {
   326  	var filter sql.Expression
   327  
   328  	node, _, _, err := b.Parse("select routine_schema as `Db`, routine_name as `Name`, routine_type as `Type`,"+
   329  		"definer as `Definer`, last_altered as `Modified`, created as `Created`, security_type as `Security_type`,"+
   330  		"routine_comment as `Comment`, CHARACTER_SET_CLIENT as `character_set_client`, COLLATION_CONNECTION as `collation_connection`,"+
   331  		"database_collation as `Database Collation` from information_schema.routines where routine_type = 'PROCEDURE'", false)
   332  	if err != nil {
   333  		b.handleErr(err)
   334  	}
   335  
   336  	outScope = inScope.push()
   337  	for _, c := range node.Schema() {
   338  		outScope.newColumn(scopeColumn{
   339  			db:    c.DatabaseSource,
   340  			table: c.Source,
   341  			col:   c.Name, typ: c.Type, nullable: c.Nullable})
   342  	}
   343  	if s.Filter != nil {
   344  		if s.Filter.Filter != nil {
   345  			filter = b.buildScalar(outScope, s.Filter.Filter)
   346  		} else if s.Filter.Like != "" {
   347  			filter = expression.NewLike(
   348  				expression.NewGetField(1, types.MustCreateString(sqltypes.VarChar, 64, sql.Collation_Information_Schema_Default), "Name", false),
   349  				expression.NewLiteral(s.Filter.Like, types.LongText),
   350  				nil,
   351  			)
   352  		}
   353  	}
   354  
   355  	if filter != nil {
   356  		node = plan.NewHaving(filter, node)
   357  	}
   358  	outScope.node = node
   359  	return
   360  }
   361  
   362  func (b *Builder) buildShowFunctionStatus(inScope *scope, s *ast.Show) (outScope *scope) {
   363  	var filter sql.Expression
   364  	node, _, _, err := b.Parse("select routine_schema as `Db`, routine_name as `Name`, routine_type as `Type`,"+
   365  		"definer as `Definer`, last_altered as `Modified`, created as `Created`, security_type as `Security_type`,"+
   366  		"routine_comment as `Comment`, character_set_client, collation_connection,"+
   367  		"database_collation as `Database Collation` from information_schema.routines where routine_type = 'FUNCTION'", false)
   368  	if err != nil {
   369  		b.handleErr(err)
   370  	}
   371  
   372  	outScope = inScope.push()
   373  	for _, c := range node.Schema() {
   374  		outScope.newColumn(scopeColumn{
   375  			db:    c.DatabaseSource,
   376  			table: c.Source,
   377  			col:   c.Name, typ: c.Type, nullable: c.Nullable})
   378  	}
   379  
   380  	if s.Filter != nil {
   381  		if s.Filter.Filter != nil {
   382  			filter = b.buildScalar(outScope, s.Filter.Filter)
   383  		} else if s.Filter.Like != "" {
   384  			filter = expression.NewLike(
   385  				expression.NewGetField(1, types.MustCreateString(sqltypes.VarChar, 64, sql.Collation_Information_Schema_Default), "Name", false),
   386  				expression.NewLiteral(s.Filter.Like, types.LongText),
   387  				nil,
   388  			)
   389  		}
   390  	}
   391  
   392  	if filter != nil {
   393  		node = plan.NewHaving(filter, node)
   394  	}
   395  	outScope.node = node
   396  	return
   397  }
   398  
   399  func (b *Builder) buildShowTableStatus(inScope *scope, s *ast.Show) (outScope *scope) {
   400  	dbName := b.ctx.GetCurrentDatabase()
   401  	if s.Database != "" {
   402  		dbName = s.Database
   403  	}
   404  
   405  	if dbName == "" {
   406  		dbName = b.ctx.GetCurrentDatabase()
   407  	}
   408  	db := b.resolveDb(dbName)
   409  
   410  	showStatus := plan.NewShowTableStatus(db)
   411  	showStatus.Catalog = b.cat
   412  	var node sql.Node = showStatus
   413  
   414  	outScope = inScope.push()
   415  	for _, c := range node.Schema() {
   416  		outScope.newColumn(scopeColumn{
   417  			db:       c.DatabaseSource,
   418  			table:    c.Source,
   419  			col:      strings.ToLower(c.Name),
   420  			typ:      c.Type,
   421  			nullable: c.Nullable,
   422  		})
   423  	}
   424  
   425  	var filter sql.Expression
   426  	if s.Filter != nil {
   427  		if s.Filter.Filter != nil {
   428  			filter = b.buildScalar(outScope, s.Filter.Filter)
   429  		} else if s.Filter.Like != "" {
   430  			filter = expression.NewLike(
   431  				expression.NewGetField(0, types.LongText, "Name", false),
   432  				expression.NewLiteral(s.Filter.Like, types.LongText),
   433  				nil,
   434  			)
   435  		}
   436  	}
   437  
   438  	if filter != nil {
   439  		node = plan.NewFilter(filter, node)
   440  	}
   441  
   442  	outScope.node = node
   443  	return
   444  }
   445  
   446  func (b *Builder) buildShowIndex(inScope *scope, s *ast.Show) (outScope *scope) {
   447  	outScope = inScope.push()
   448  	dbName := strings.ToLower(s.Database)
   449  	if dbName == "" {
   450  		dbName = s.Table.Qualifier.String()
   451  	}
   452  	if dbName == "" {
   453  		dbName = b.ctx.GetCurrentDatabase()
   454  	}
   455  	tableName := strings.ToLower(s.Table.Name.String())
   456  	tableScope, ok := b.buildResolvedTable(inScope, strings.ToLower(dbName), tableName, nil)
   457  	if !ok {
   458  		err := sql.ErrTableNotFound.New(tableName)
   459  		b.handleErr(err)
   460  	}
   461  	showIdx := plan.NewShowIndexes(tableScope.node)
   462  	switch n := tableScope.node.(type) {
   463  	case *plan.ResolvedTable:
   464  		showIdx.IndexesToShow = b.getInfoSchemaIndexes(n)
   465  	case *plan.SubqueryAlias:
   466  		// views don't have keys
   467  		showIdx.Child = plan.NewResolvedDualTable()
   468  	default:
   469  		err := sql.ErrTableNotFound.New(tableName)
   470  		b.handleErr(err)
   471  	}
   472  	outScope.node = showIdx
   473  	return
   474  }
   475  
   476  func (b *Builder) getInfoSchemaIndexes(rt *plan.ResolvedTable) []sql.Index {
   477  	it, ok := rt.Table.(sql.IndexAddressableTable)
   478  	if !ok {
   479  		return nil
   480  	}
   481  
   482  	indexes, err := it.GetIndexes(b.ctx)
   483  	if err != nil {
   484  		b.handleErr(err)
   485  	}
   486  
   487  	for i := 0; i < len(indexes); i++ {
   488  		// remove generated indexes
   489  		idx := indexes[i]
   490  		if idx.IsGenerated() {
   491  			indexes[i], indexes[len(indexes)-1] = indexes[len(indexes)-1], indexes[i]
   492  			indexes = indexes[:len(indexes)-1]
   493  			i--
   494  		}
   495  	}
   496  
   497  	if b.ctx.GetIndexRegistry().HasIndexes() {
   498  		idxRegistry := b.ctx.GetIndexRegistry()
   499  		for _, idx := range idxRegistry.IndexesByTable(rt.Database().Name(), rt.Table.Name()) {
   500  			if !idx.IsGenerated() {
   501  				indexes = append(indexes, idx)
   502  			}
   503  		}
   504  	}
   505  
   506  	return indexes
   507  }
   508  
   509  func (b *Builder) buildShowVariables(inScope *scope, s *ast.Show) (outScope *scope) {
   510  	outScope = inScope.push()
   511  	dummy := &plan.ShowVariables{}
   512  	for _, c := range dummy.Schema() {
   513  		outScope.newColumn(scopeColumn{
   514  			db:       strings.ToLower(c.DatabaseSource),
   515  			table:    strings.ToLower(c.Source),
   516  			col:      strings.ToLower(c.Name),
   517  			typ:      c.Type,
   518  			nullable: c.Nullable,
   519  		})
   520  	}
   521  
   522  	var filter sql.Expression
   523  	var like sql.Expression
   524  	if s.Filter != nil {
   525  		if s.Filter.Filter != nil {
   526  			filter = b.buildScalar(outScope, s.Filter.Filter)
   527  		}
   528  		if s.Filter.Like != "" {
   529  			like = expression.NewLike(
   530  				expression.NewGetField(0, types.LongText, "variable_name", false),
   531  				expression.NewLiteral(s.Filter.Like, types.LongText),
   532  				nil,
   533  			)
   534  			if filter != nil {
   535  				filter = expression.NewAnd(like, filter)
   536  			} else {
   537  				filter = like
   538  			}
   539  		}
   540  	}
   541  	if filter == nil {
   542  		filter = expression.NewLiteral(true, types.Boolean)
   543  	}
   544  	outScope.node = plan.NewShowVariables(filter, strings.ToLower(s.Scope) == "global")
   545  
   546  	return
   547  }
   548  
   549  func (b *Builder) buildAsOfLit(inScope *scope, t ast.Expr) interface{} {
   550  	expr := b.buildAsOfExpr(inScope, t)
   551  	res, err := expr.Eval(b.ctx, nil)
   552  	if err != nil {
   553  		b.handleErr(err)
   554  	}
   555  	switch res.(type) {
   556  	case string, time.Time:
   557  		return res
   558  	}
   559  
   560  	if res != nil {
   561  		err = sql.ErrInvalidAsOfExpression.New(res)
   562  	} else {
   563  		err = sql.ErrInvalidAsOfExpression.New(t)
   564  	}
   565  	b.handleErr(err)
   566  	return nil
   567  }
   568  
   569  func (b *Builder) buildAsOfExpr(inScope *scope, time ast.Expr) sql.Expression {
   570  	switch v := time.(type) {
   571  	case *ast.SQLVal:
   572  		if v.Type == ast.ValArg && (b.bindCtx == nil || b.bindCtx.resolveOnly) {
   573  			return nil
   574  		}
   575  		repl := b.normalizeValArg(v)
   576  		val, ok := repl.(*ast.SQLVal)
   577  		if !ok {
   578  			// *ast.NullVal
   579  			return nil
   580  		}
   581  		ret, _, err := types.Text.Convert(val.Val)
   582  		if err != nil {
   583  			b.handleErr(err)
   584  		}
   585  		return expression.NewLiteral(ret.(string), types.LongText)
   586  	case *ast.ColName:
   587  		sysVar, _, ok := b.buildSysVar(v, ast.SetScope_None)
   588  		if ok {
   589  			return sysVar
   590  		}
   591  		return expression.NewLiteral(v.String(), types.LongText)
   592  	case *ast.FuncExpr:
   593  		// todo(max): more specific validation for nested ASOF functions
   594  		if isWindowFunc(v.Name.Lowered()) || isAggregateFunc(v.Name.Lowered()) {
   595  			err := sql.ErrInvalidAsOfExpression.New(v)
   596  			b.handleErr(err)
   597  		}
   598  	default:
   599  	}
   600  	return b.buildScalar(b.newScope(), time)
   601  }
   602  
   603  func (b *Builder) buildShowAllTables(inScope *scope, s *ast.Show) (outScope *scope) {
   604  	outScope = inScope.push()
   605  
   606  	var dbName string
   607  	var filter sql.Expression
   608  	var asOf sql.Expression
   609  	if s.ShowTablesOpt != nil {
   610  		dbName = s.ShowTablesOpt.DbName
   611  		if s.ShowTablesOpt.AsOf != nil {
   612  			asOf = b.buildAsOfExpr(inScope, s.ShowTablesOpt.AsOf)
   613  		}
   614  	}
   615  
   616  	if dbName == "" {
   617  		dbName = b.ctx.GetCurrentDatabase()
   618  	}
   619  	db := b.resolveDb(dbName)
   620  
   621  	showTabs := plan.NewShowTables(db, s.Full, asOf)
   622  	for _, c := range showTabs.Schema() {
   623  		outScope.newColumn(scopeColumn{
   624  			db:    strings.ToLower(c.DatabaseSource),
   625  			table: strings.ToLower(c.Source),
   626  			col:   c.Name, typ: c.Type, nullable: c.Nullable})
   627  	}
   628  
   629  	if s.ShowTablesOpt.Filter != nil {
   630  		if s.ShowTablesOpt.Filter.Filter != nil {
   631  			filter = b.buildScalar(outScope, s.ShowTablesOpt.Filter.Filter)
   632  		} else if s.ShowTablesOpt.Filter.Like != "" {
   633  			filter = expression.NewLike(
   634  				expression.NewGetField(0, types.LongText, fmt.Sprintf("Tables_in_%s", dbName), false),
   635  				expression.NewLiteral(s.ShowTablesOpt.Filter.Like, types.LongText),
   636  				nil,
   637  			)
   638  		}
   639  	}
   640  
   641  	outScope.node = showTabs
   642  
   643  	if filter != nil {
   644  		outScope.node = plan.NewFilter(filter, outScope.node)
   645  	}
   646  
   647  	return
   648  }
   649  
   650  func (b *Builder) buildShowAllDatabases(inScope *scope, s *ast.Show) (outScope *scope) {
   651  	showDbs := plan.NewShowDatabases()
   652  	showDbs.Catalog = b.cat
   653  	outScope = inScope.push()
   654  	for _, c := range showDbs.Schema() {
   655  		outScope.newColumn(scopeColumn{
   656  			db:    strings.ToLower(c.DatabaseSource),
   657  			table: strings.ToLower(c.Source),
   658  			col:   c.Name, typ: c.Type, nullable: c.Nullable})
   659  	}
   660  	var filter sql.Expression
   661  	if s.Filter != nil {
   662  		if s.Filter.Filter != nil {
   663  			filter = b.buildScalar(outScope, s.Filter.Filter)
   664  		} else if s.Filter.Like != "" {
   665  			filter = expression.NewLike(
   666  				expression.NewGetField(0, types.LongText, "Database", false),
   667  				expression.NewLiteral(s.Filter.Like, types.LongText),
   668  				nil,
   669  			)
   670  		}
   671  	}
   672  	outScope.node = showDbs
   673  	if filter != nil {
   674  		outScope.node = plan.NewFilter(filter, outScope.node)
   675  	}
   676  	return
   677  }
   678  
   679  func (b *Builder) buildShowAllColumns(inScope *scope, s *ast.Show) (outScope *scope) {
   680  	outScope = inScope.push()
   681  	full := s.Full
   682  	var table sql.Node
   683  
   684  	var asOf *ast.AsOf
   685  	if s.ShowTablesOpt != nil && s.ShowTablesOpt.AsOf != nil {
   686  		asOf = &ast.AsOf{Time: s.ShowTablesOpt.AsOf}
   687  	}
   688  
   689  	db := s.Database
   690  	if db == "" {
   691  		db = s.Table.Qualifier.String()
   692  	}
   693  	if db == "" {
   694  		db = b.currentDb().Name()
   695  	}
   696  
   697  	tableName := strings.ToLower(s.Table.Name.String())
   698  	tableScope, ok := b.buildResolvedTable(inScope, db, tableName, asOf)
   699  	if !ok {
   700  		err := sql.ErrTableNotFound.New(tableName)
   701  		b.handleErr(err)
   702  	}
   703  	table = tableScope.node
   704  
   705  	show := plan.NewShowColumns(full, table)
   706  
   707  	for _, c := range show.Schema() {
   708  		outScope.newColumn(scopeColumn{
   709  			db:       strings.ToLower(c.DatabaseSource),
   710  			table:    strings.ToLower(c.Source),
   711  			col:      strings.ToLower(c.Name),
   712  			typ:      c.Type,
   713  			nullable: c.Nullable,
   714  		})
   715  	}
   716  
   717  	var node sql.Node = show
   718  	switch t := table.(type) {
   719  	case *plan.ResolvedTable:
   720  		show.Indexes = b.getInfoSchemaIndexes(t)
   721  		node = b.modifySchemaTarget(tableScope, show, t)
   722  	case *plan.SubqueryAlias:
   723  		var err error
   724  		node, err = show.WithTargetSchema(t.Schema())
   725  		if err != nil {
   726  			b.handleErr(err)
   727  		}
   728  	default:
   729  	}
   730  
   731  	if s.ShowTablesOpt != nil && s.ShowTablesOpt.Filter != nil {
   732  		if s.ShowTablesOpt.Filter.Like != "" {
   733  			pattern := expression.NewLiteral(s.ShowTablesOpt.Filter.Like, types.LongText)
   734  
   735  			node = plan.NewFilter(
   736  				expression.NewLike(
   737  					expression.NewGetField(0, plan.VarChar25000, "Field", false),
   738  					pattern,
   739  					nil,
   740  				),
   741  				node,
   742  			)
   743  		}
   744  
   745  		if s.ShowTablesOpt.Filter.Filter != nil {
   746  			filter := b.buildScalar(outScope, s.ShowTablesOpt.Filter.Filter)
   747  			node = plan.NewFilter(filter, node)
   748  		}
   749  	}
   750  
   751  	outScope.node = node
   752  	return
   753  }
   754  
   755  func (b *Builder) buildShowWarnings(inScope *scope, s *ast.Show) (outScope *scope) {
   756  	outScope = inScope.push()
   757  	if s.CountStar {
   758  		unsupportedShow := "SHOW COUNT(*) WARNINGS"
   759  		b.handleErr(sql.ErrUnsupportedFeature.New(unsupportedShow))
   760  	}
   761  	var node sql.Node
   762  	node = plan.ShowWarnings(b.ctx.Session.Warnings())
   763  	if s.Limit != nil {
   764  		if s.Limit.Offset != nil {
   765  			offset := b.buildScalar(inScope, s.Limit.Offset)
   766  			node = plan.NewOffset(offset, node)
   767  		}
   768  		limit := b.buildScalar(inScope, s.Limit.Rowcount)
   769  		node = plan.NewLimit(limit, node)
   770  	}
   771  
   772  	outScope.node = node
   773  	return
   774  }
   775  
   776  func (b *Builder) buildShowCollation(inScope *scope, s *ast.Show) (outScope *scope) {
   777  	outScope = inScope.push()
   778  	// show collation statements are functionally identical to selecting from the collations table in
   779  	// information_schema, with slightly different syntax and with some columns aliased.
   780  	// TODO: install information_schema automatically for all catalogs
   781  	node, _, _, err := b.Parse("select collation_name as `collation`, character_set_name as charset, id,"+
   782  		"is_default as `default`, is_compiled as compiled, sortlen, pad_attribute from information_schema.collations order by collation_name", false)
   783  	if err != nil {
   784  		b.handleErr(err)
   785  	}
   786  
   787  	for _, c := range node.Schema() {
   788  		outScope.newColumn(scopeColumn{
   789  			db:       strings.ToLower(c.DatabaseSource),
   790  			table:    strings.ToLower(c.Source),
   791  			col:      strings.ToLower(c.Name),
   792  			typ:      c.Type,
   793  			nullable: c.Nullable,
   794  		})
   795  	}
   796  
   797  	if s.ShowCollationFilterOpt != nil {
   798  		filterExpr := b.buildScalar(outScope, s.ShowCollationFilterOpt)
   799  		// TODO: once collations are properly implemented, we should better be able to handle utf8 -> utf8mb3 comparisons as they're aliases
   800  		filterExpr, _, _ = transform.Expr(filterExpr, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
   801  			if exprLiteral, ok := expr.(*expression.Literal); ok {
   802  				const utf8Prefix = "utf8_"
   803  				if strLiteral, ok := exprLiteral.Value().(string); ok && strings.HasPrefix(strLiteral, utf8Prefix) {
   804  					return expression.NewLiteral("utf8mb3_"+strLiteral[len(utf8Prefix):], exprLiteral.Type()), transform.NewTree, nil
   805  				}
   806  			}
   807  			return expr, transform.SameTree, nil
   808  		})
   809  		node = plan.NewHaving(filterExpr, node)
   810  	}
   811  
   812  	outScope.node = node
   813  	return
   814  }
   815  
   816  func (b *Builder) buildShowEngines(inScope *scope, s *ast.Show) (outScope *scope) {
   817  	outScope = inScope.push()
   818  	infoSchemaSelect, _, _, err := b.Parse("select * from information_schema.engines", false)
   819  	if err != nil {
   820  		b.handleErr(err)
   821  	}
   822  
   823  	outScope.node = infoSchemaSelect
   824  	return
   825  }
   826  
   827  func (b *Builder) buildShowPlugins(inScope *scope, s *ast.Show) (outScope *scope) {
   828  	outScope = inScope.push()
   829  	infoSchemaSelect, _, _, err := b.Parse("select * from information_schema.plugins", false)
   830  	if err != nil {
   831  		b.handleErr(err)
   832  	}
   833  
   834  	outScope.node = infoSchemaSelect
   835  	return
   836  }
   837  
   838  func (b *Builder) buildShowStatus(inScope *scope, s *ast.Show) (outScope *scope) {
   839  	outScope = inScope.push()
   840  	var node sql.Node
   841  	if s.Scope == ast.GlobalStr {
   842  		node = plan.NewShowStatus(plan.ShowStatusModifier_Global)
   843  	} else {
   844  		node = plan.NewShowStatus(plan.ShowStatusModifier_Session)
   845  	}
   846  
   847  	for _, c := range node.Schema() {
   848  		outScope.newColumn(scopeColumn{
   849  			db:       strings.ToLower(c.DatabaseSource),
   850  			table:    strings.ToLower(c.Source),
   851  			col:      strings.ToLower(c.Name),
   852  			typ:      c.Type,
   853  			nullable: c.Nullable,
   854  		})
   855  	}
   856  
   857  	var filter sql.Expression
   858  	if s.Filter != nil {
   859  		if s.Filter.Like != "" {
   860  			filter = expression.NewLike(
   861  				expression.NewGetField(0, node.Schema()[0].Type, plan.ShowStatusVariableCol, false),
   862  				expression.NewLiteral(s.Filter.Like, types.LongText),
   863  				nil,
   864  			)
   865  		} else if s.Filter.Filter != nil {
   866  			filter = b.buildScalar(outScope, s.Filter.Filter)
   867  		}
   868  	}
   869  
   870  	if filter != nil {
   871  		node = plan.NewFilter(filter, node)
   872  	}
   873  
   874  	outScope.node = node
   875  
   876  	return
   877  }
   878  
   879  func (b *Builder) buildShowCharset(inScope *scope, s *ast.Show) (outScope *scope) {
   880  	outScope = inScope.push()
   881  
   882  	showCharset := plan.NewShowCharset()
   883  	showCharset.CharacterSetTable = b.resolveTable("character_sets", "information_schema", nil)
   884  
   885  	var node sql.Node = showCharset
   886  	for _, c := range node.Schema() {
   887  		outScope.newColumn(scopeColumn{
   888  			db:    strings.ToLower(c.DatabaseSource),
   889  			table: strings.ToLower(c.Source),
   890  			col:   c.Name, typ: c.Type, nullable: c.Nullable})
   891  	}
   892  
   893  	var filter sql.Expression
   894  	if s.Filter != nil {
   895  		if s.Filter.Filter != nil {
   896  			filter = b.buildScalar(outScope, s.Filter.Filter)
   897  		} else if s.Filter.Like != "" {
   898  			filter = expression.NewLike(
   899  				expression.NewGetField(0, types.MustCreateStringWithDefaults(sqltypes.VarChar, 64), "Charset", false),
   900  				expression.NewLiteral(s.Filter.Like, types.LongText),
   901  				nil,
   902  			)
   903  		}
   904  	}
   905  
   906  	if filter != nil {
   907  		node = plan.NewFilter(filter, node)
   908  	}
   909  	outScope.node = node
   910  	return
   911  }