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 }