github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/executor/show.go (about) 1 // Copyright 2016 PingCAP, 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package executor 15 16 import ( 17 "bytes" 18 "fmt" 19 "sort" 20 "strings" 21 22 "github.com/insionng/yougam/libraries/juju/errors" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/column" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/context" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/infoschema" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 28 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 29 "github.com/insionng/yougam/libraries/pingcap/tidb/privilege" 30 "github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/variable" 31 "github.com/insionng/yougam/libraries/pingcap/tidb/table" 32 "github.com/insionng/yougam/libraries/pingcap/tidb/util/charset" 33 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 34 ) 35 36 // ShowExec represents a show executor. 37 type ShowExec struct { 38 Tp ast.ShowStmtType // Databases/Tables/Columns/.... 39 DBName model.CIStr 40 Table *ast.TableName // Used for showing columns. 41 Column *ast.ColumnName // Used for `desc table column`. 42 Flag int // Some flag parsed from sql, such as FULL. 43 Full bool 44 User string // Used for show grants. 45 46 // Used by show variables 47 GlobalScope bool 48 49 fields []*ast.ResultField 50 ctx context.Context 51 is infoschema.InfoSchema 52 53 fetched bool 54 rows []*Row 55 cursor int 56 } 57 58 // Fields implements Executor Fields interface. 59 func (e *ShowExec) Fields() []*ast.ResultField { 60 return e.fields 61 } 62 63 // Next implements Execution Next interface. 64 func (e *ShowExec) Next() (*Row, error) { 65 if e.rows == nil { 66 err := e.fetchAll() 67 if err != nil { 68 return nil, errors.Trace(err) 69 } 70 } 71 if e.cursor >= len(e.rows) { 72 return nil, nil 73 } 74 row := e.rows[e.cursor] 75 for i, field := range e.fields { 76 field.Expr.SetValue(row.Data[i].GetValue()) 77 } 78 e.cursor++ 79 return row, nil 80 } 81 82 func (e *ShowExec) fetchAll() error { 83 switch e.Tp { 84 case ast.ShowCharset: 85 return e.fetchShowCharset() 86 case ast.ShowCollation: 87 return e.fetchShowCollation() 88 case ast.ShowColumns: 89 return e.fetchShowColumns() 90 case ast.ShowCreateTable: 91 return e.fetchShowCreateTable() 92 case ast.ShowDatabases: 93 return e.fetchShowDatabases() 94 case ast.ShowEngines: 95 return e.fetchShowEngines() 96 case ast.ShowGrants: 97 return e.fetchShowGrants() 98 case ast.ShowIndex: 99 return e.fetchShowIndex() 100 case ast.ShowProcedureStatus: 101 return e.fetchShowProcedureStatus() 102 case ast.ShowStatus: 103 return e.fetchShowStatus() 104 case ast.ShowTables: 105 return e.fetchShowTables() 106 case ast.ShowTableStatus: 107 return e.fetchShowTableStatus() 108 case ast.ShowTriggers: 109 return e.fetchShowTriggers() 110 case ast.ShowVariables: 111 return e.fetchShowVariables() 112 case ast.ShowWarnings: 113 // empty result 114 } 115 return nil 116 } 117 118 func (e *ShowExec) fetchShowEngines() error { 119 row := &Row{ 120 Data: types.MakeDatums( 121 "InnoDB", 122 "DEFAULT", 123 "Supports transactions, row-level locking, and foreign keys", 124 "YES", 125 "YES", 126 "YES", 127 ), 128 } 129 e.rows = append(e.rows, row) 130 return nil 131 } 132 133 func (e *ShowExec) fetchShowDatabases() error { 134 dbs := e.is.AllSchemaNames() 135 // TODO: let information_schema be the first database 136 sort.Strings(dbs) 137 for _, d := range dbs { 138 e.rows = append(e.rows, &Row{Data: types.MakeDatums(d)}) 139 } 140 return nil 141 } 142 143 func (e *ShowExec) fetchShowTables() error { 144 if !e.is.SchemaExists(e.DBName) { 145 return errors.Errorf("Can not find DB: %s", e.DBName) 146 } 147 // sort for tables 148 var tableNames []string 149 for _, v := range e.is.SchemaTables(e.DBName) { 150 tableNames = append(tableNames, v.Meta().Name.L) 151 } 152 sort.Strings(tableNames) 153 for _, v := range tableNames { 154 data := types.MakeDatums(v) 155 if e.Full { 156 // TODO: support "VIEW" later if we have supported view feature. 157 // now, just use "BASE TABLE". 158 data = append(data, types.NewDatum("BASE TABLE")) 159 } 160 e.rows = append(e.rows, &Row{Data: data}) 161 } 162 return nil 163 } 164 165 func (e *ShowExec) fetchShowTableStatus() error { 166 if !e.is.SchemaExists(e.DBName) { 167 return errors.Errorf("Can not find DB: %s", e.DBName) 168 } 169 170 // sort for tables 171 var tableNames []string 172 for _, v := range e.is.SchemaTables(e.DBName) { 173 tableNames = append(tableNames, v.Meta().Name.L) 174 } 175 sort.Strings(tableNames) 176 177 for _, v := range tableNames { 178 now := mysql.CurrentTime(mysql.TypeDatetime) 179 data := types.MakeDatums(v, "InnoDB", "10", "Compact", 100, 100, 100, 100, 100, 100, 100, 180 now, now, now, "utf8_general_ci", "", "", "") 181 e.rows = append(e.rows, &Row{Data: data}) 182 } 183 return nil 184 } 185 186 func (e *ShowExec) fetchShowColumns() error { 187 tb, err := e.getTable() 188 if err != nil { 189 return errors.Trace(err) 190 } 191 cols := tb.Cols() 192 for _, col := range cols { 193 if e.Column != nil && e.Column.Name.L != col.Name.L { 194 continue 195 } 196 197 desc := column.NewColDesc(col) 198 199 // The FULL keyword causes the output to include the column collation and comments, 200 // as well as the privileges you have for each column. 201 row := &Row{} 202 if e.Full { 203 row.Data = types.MakeDatums( 204 desc.Field, 205 desc.Type, 206 desc.Collation, 207 desc.Null, 208 desc.Key, 209 desc.DefaultValue, 210 desc.Extra, 211 desc.Privileges, 212 desc.Comment, 213 ) 214 } else { 215 row.Data = types.MakeDatums( 216 desc.Field, 217 desc.Type, 218 desc.Null, 219 desc.Key, 220 desc.DefaultValue, 221 desc.Extra, 222 ) 223 } 224 e.rows = append(e.rows, row) 225 } 226 return nil 227 } 228 229 func (e *ShowExec) fetchShowIndex() error { 230 tb, err := e.getTable() 231 if err != nil { 232 return errors.Trace(err) 233 } 234 for _, idx := range tb.Indices() { 235 for i, col := range idx.Columns { 236 nonUniq := 1 237 if idx.Unique { 238 nonUniq = 0 239 } 240 var subPart interface{} 241 if col.Length != types.UnspecifiedLength { 242 subPart = col.Length 243 } 244 data := types.MakeDatums( 245 tb.Meta().Name.O, // Table 246 nonUniq, // Non_unique 247 idx.Name.O, // Key_name 248 i+1, // Seq_in_index 249 col.Name.O, // Column_name 250 "utf8_bin", // Colation 251 0, // Cardinality 252 subPart, // Sub_part 253 nil, // Packed 254 "YES", // Null 255 idx.Tp.String(), // Index_type 256 "", // Comment 257 idx.Comment, // Index_comment 258 ) 259 e.rows = append(e.rows, &Row{Data: data}) 260 } 261 } 262 return nil 263 } 264 265 func (e *ShowExec) fetchShowCharset() error { 266 // See: http://dev.mysql.com/doc/refman/5.7/en/show-character-set.html 267 descs := charset.GetAllCharsets() 268 for _, desc := range descs { 269 row := &Row{ 270 Data: types.MakeDatums( 271 desc.Name, 272 desc.Desc, 273 desc.DefaultCollation, 274 desc.Maxlen, 275 ), 276 } 277 e.rows = append(e.rows, row) 278 } 279 return nil 280 } 281 282 func (e *ShowExec) fetchShowVariables() error { 283 sessionVars := variable.GetSessionVars(e.ctx) 284 globalVars := variable.GetGlobalVarAccessor(e.ctx) 285 for _, v := range variable.SysVars { 286 var err error 287 var value string 288 if !e.GlobalScope { 289 // Try to get Session Scope variable value first. 290 sv, ok := sessionVars.Systems[v.Name] 291 if ok { 292 value = sv 293 } else { 294 // If session scope variable is not set, get the global scope value. 295 value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name) 296 if err != nil { 297 return errors.Trace(err) 298 } 299 } 300 } else { 301 value, err = globalVars.GetGlobalSysVar(e.ctx, v.Name) 302 if err != nil { 303 return errors.Trace(err) 304 } 305 } 306 row := &Row{Data: types.MakeDatums(v.Name, value)} 307 e.rows = append(e.rows, row) 308 } 309 return nil 310 } 311 312 func (e *ShowExec) fetchShowStatus() error { 313 statusVars, err := variable.GetStatusVars() 314 if err != nil { 315 return errors.Trace(err) 316 } 317 for status, v := range statusVars { 318 if e.GlobalScope && v.Scope == variable.ScopeSession { 319 continue 320 } 321 switch v.Value.(type) { 322 case []interface{}, nil: 323 v.Value = fmt.Sprintf("%v", v.Value) 324 } 325 value, err := types.ToString(v.Value) 326 if err != nil { 327 return errors.Trace(err) 328 } 329 row := &Row{Data: types.MakeDatums(status, value)} 330 e.rows = append(e.rows, row) 331 } 332 return nil 333 } 334 335 func (e *ShowExec) fetchShowCreateTable() error { 336 tb, err := e.getTable() 337 if err != nil { 338 return errors.Trace(err) 339 } 340 341 // TODO: let the result more like MySQL. 342 var buf bytes.Buffer 343 buf.WriteString(fmt.Sprintf("CREATE TABLE `%s` (\n", tb.Meta().Name.O)) 344 var pkCol *column.Col 345 for i, col := range tb.Cols() { 346 buf.WriteString(fmt.Sprintf(" `%s` %s", col.Name.O, col.GetTypeDesc())) 347 if mysql.HasAutoIncrementFlag(col.Flag) { 348 buf.WriteString(" NOT NULL AUTO_INCREMENT") 349 } else { 350 if mysql.HasNotNullFlag(col.Flag) { 351 buf.WriteString(" NOT NULL") 352 } 353 if !mysql.HasNoDefaultValueFlag(col.Flag) { 354 switch col.DefaultValue { 355 case nil: 356 buf.WriteString(" DEFAULT NULL") 357 case "CURRENT_TIMESTAMP": 358 buf.WriteString(" DEFAULT CURRENT_TIMESTAMP") 359 default: 360 buf.WriteString(fmt.Sprintf(" DEFAULT '%v'", col.DefaultValue)) 361 } 362 } 363 if mysql.HasOnUpdateNowFlag(col.Flag) { 364 buf.WriteString(" ON UPDATE CURRENT_TIMESTAMP") 365 } 366 } 367 if len(col.Comment) > 0 { 368 buf.WriteString(fmt.Sprintf(" COMMENT '%s'", col.Comment)) 369 } 370 if i != len(tb.Cols())-1 { 371 buf.WriteString(",\n") 372 } 373 if tb.Meta().PKIsHandle && mysql.HasPriKeyFlag(col.Flag) { 374 pkCol = col 375 } 376 } 377 378 if pkCol != nil { 379 // If PKIsHanle, pk info is not in tb.Indices(). We should handle it here. 380 buf.WriteString(",\n") 381 buf.WriteString(fmt.Sprintf(" PRIMARY KEY (`%s`) ", pkCol.Name.O)) 382 } 383 384 if len(tb.Indices()) > 0 { 385 buf.WriteString(",\n") 386 } 387 388 for i, idx := range tb.Indices() { 389 if idx.Primary { 390 buf.WriteString(" PRIMARY KEY ") 391 } else if idx.Unique { 392 buf.WriteString(fmt.Sprintf(" UNIQUE KEY `%s` ", idx.Name.O)) 393 } else { 394 buf.WriteString(fmt.Sprintf(" KEY `%s` ", idx.Name.O)) 395 } 396 397 cols := make([]string, 0, len(idx.Columns)) 398 for _, c := range idx.Columns { 399 cols = append(cols, c.Name.O) 400 } 401 buf.WriteString(fmt.Sprintf("(`%s`)", strings.Join(cols, "`,`"))) 402 if i != len(tb.Indices())-1 { 403 buf.WriteString(",\n") 404 } 405 } 406 407 for _, fk := range tb.Meta().ForeignKeys { 408 if fk.State != model.StatePublic { 409 continue 410 } 411 412 buf.WriteString("\n") 413 cols := make([]string, 0, len(fk.Cols)) 414 for _, c := range fk.Cols { 415 cols = append(cols, c.L) 416 } 417 418 refCols := make([]string, 0, len(fk.RefCols)) 419 for _, c := range fk.Cols { 420 refCols = append(refCols, c.L) 421 } 422 423 buf.WriteString(fmt.Sprintf(" CONSTRAINT `%s` FOREIGN KEY (`%s`)", fk.Name.L, strings.Join(cols, "`,`"))) 424 buf.WriteString(fmt.Sprintf(" REFERENCES `%s` (`%s`)", fk.RefTable.L, strings.Join(refCols, "`,`"))) 425 426 if ast.ReferOptionType(fk.OnDelete) != ast.ReferOptionNoOption { 427 buf.WriteString(fmt.Sprintf(" ON DELETE %s", ast.ReferOptionType(fk.OnDelete))) 428 } 429 430 if ast.ReferOptionType(fk.OnUpdate) != ast.ReferOptionNoOption { 431 buf.WriteString(fmt.Sprintf(" ON UPDATE %s", ast.ReferOptionType(fk.OnUpdate))) 432 } 433 } 434 buf.WriteString("\n") 435 436 buf.WriteString(") ENGINE=InnoDB") 437 if s := tb.Meta().Charset; len(s) > 0 { 438 buf.WriteString(fmt.Sprintf(" DEFAULT CHARSET=%s", s)) 439 } 440 441 if tb.Meta().AutoIncID > 0 { 442 buf.WriteString(fmt.Sprintf(" AUTO_INCREMENT=%d", tb.Meta().AutoIncID)) 443 } 444 445 if len(tb.Meta().Comment) > 0 { 446 buf.WriteString(fmt.Sprintf(" COMMENT='%s'", tb.Meta().Comment)) 447 } 448 449 data := types.MakeDatums(tb.Meta().Name.O, buf.String()) 450 e.rows = append(e.rows, &Row{Data: data}) 451 return nil 452 } 453 454 func (e *ShowExec) fetchShowCollation() error { 455 collations := charset.GetCollations() 456 for _, v := range collations { 457 isDefault := "" 458 if v.IsDefault { 459 isDefault = "Yes" 460 } 461 row := &Row{Data: types.MakeDatums( 462 v.Name, 463 v.CharsetName, 464 v.ID, 465 isDefault, 466 "Yes", 467 1, 468 )} 469 e.rows = append(e.rows, row) 470 } 471 return nil 472 } 473 474 func (e *ShowExec) fetchShowGrants() error { 475 // Get checker 476 checker := privilege.GetPrivilegeChecker(e.ctx) 477 if checker == nil { 478 return errors.New("Miss privilege checker!") 479 } 480 gs, err := checker.ShowGrants(e.ctx, e.User) 481 if err != nil { 482 return errors.Trace(err) 483 } 484 for _, g := range gs { 485 data := types.MakeDatums(g) 486 e.rows = append(e.rows, &Row{Data: data}) 487 } 488 return nil 489 } 490 491 func (e *ShowExec) fetchShowTriggers() error { 492 return nil 493 } 494 495 func (e *ShowExec) fetchShowProcedureStatus() error { 496 return nil 497 } 498 499 func (e *ShowExec) getTable() (table.Table, error) { 500 if e.Table == nil { 501 return nil, errors.New("table not found") 502 } 503 tb, ok := e.is.TableByID(e.Table.TableInfo.ID) 504 if !ok { 505 return nil, errors.Errorf("table %s not found", e.Table.Name) 506 } 507 return tb, nil 508 } 509 510 // Close implements Executor Close interface. 511 func (e *ShowExec) Close() error { 512 return nil 513 }