github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/build_show.go (about) 1 // Copyright 2021 - 2022 Matrix Origin 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 plan 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "strings" 22 23 "github.com/matrixorigin/matrixone/pkg/sql/util" 24 25 "github.com/matrixorigin/matrixone/pkg/catalog" 26 "github.com/matrixorigin/matrixone/pkg/common/moerr" 27 "github.com/matrixorigin/matrixone/pkg/container/types" 28 "github.com/matrixorigin/matrixone/pkg/pb/plan" 29 "github.com/matrixorigin/matrixone/pkg/sql/parsers" 30 "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" 31 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 32 ) 33 34 const MO_CATALOG_DB_NAME = "mo_catalog" 35 const MO_DEFUALT_HOSTNAME = "localhost" 36 const INFORMATION_SCHEMA = "information_schema" 37 38 func buildShowCreateDatabase(stmt *tree.ShowCreateDatabase, 39 ctx CompilerContext) (*Plan, error) { 40 if !ctx.DatabaseExists(stmt.Name) { 41 return nil, moerr.NewBadDB(ctx.GetContext(), stmt.Name) 42 } 43 44 // get data from schema 45 //sql := fmt.Sprintf("SELECT md.datname as `Database` FROM %s.mo_database md WHERE md.datname = '%s'", MO_CATALOG_DB_NAME, stmt.Name) 46 // sql := fmt.Sprintf("SELECT md.datname as `Database`,dat_createsql as `Create Database` FROM %s.mo_database md WHERE md.datname = '%s'", MO_CATALOG_DB_NAME, stmt.Name) 47 // return returnByRewriteSQL(ctx, sql, plan.DataDefinition_SHOW_CREATEDATABASE) 48 49 sqlStr := "select \"%s\" as `Database`, \"%s\" as `Create Database`" 50 createSql := fmt.Sprintf("CREATE DATABASE `%s`", stmt.Name) 51 sqlStr = fmt.Sprintf(sqlStr, stmt.Name, createSql) 52 // logutil.Info(sqlStr) 53 54 return returnByRewriteSQL(ctx, sqlStr, plan.DataDefinition_SHOW_CREATEDATABASE) 55 } 56 57 func buildShowCreateTable(stmt *tree.ShowCreateTable, ctx CompilerContext) (*Plan, error) { 58 tblName := stmt.Name.Parts[0] 59 dbName := ctx.DefaultDatabase() 60 if stmt.Name.NumParts == 2 { 61 dbName = stmt.Name.Parts[1] 62 } 63 64 _, tableDef := ctx.Resolve(dbName, tblName) 65 if tableDef == nil { 66 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 67 } 68 if tableDef.TableType == catalog.SystemViewRel { 69 newStmt := tree.NewShowCreateView(tree.SetUnresolvedObjectName(1, [3]string{tblName, "", ""})) 70 return buildShowCreateView(newStmt, ctx) 71 } 72 73 // sql := ` 74 // SELECT * 75 // FROM %s.mo_tables mt JOIN %s.mo_columns mc 76 // ON mt.relname = mc.att_relname and mt.reldatabase=mc.att_database 77 // WHERE mt.reldatabase = '%s' AND mt.relname = '%s' 78 // ` 79 // sql = fmt.Sprintf(sql, MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, dbName, tblName) 80 // logutil.Info(sql) 81 82 var createStr string 83 if tableDef.TableType == catalog.SystemOrdinaryRel { 84 createStr = fmt.Sprintf("CREATE TABLE `%s` (", tblName) 85 } else if tableDef.TableType == catalog.SystemExternalRel { 86 createStr = fmt.Sprintf("CREATE EXTERNAL TABLE `%s` (", tblName) 87 } 88 rowCount := 0 89 var pkDefs []string 90 isClusterTable := util.TableIsClusterTable(tableDef.TableType) 91 92 colIdToName := make(map[uint64]string) 93 for _, col := range tableDef.Cols { 94 colName := col.Name 95 colIdToName[col.ColId] = col.Name 96 if colName == catalog.Row_ID { 97 continue 98 } 99 //the non-sys account skips the column account_id of the cluster table 100 if util.IsClusterTableAttribute(colName) && 101 isClusterTable && 102 ctx.GetAccountId() != catalog.System_Account { 103 continue 104 } 105 nullOrNot := "NOT NULL" 106 // col.Default must be not nil 107 if len(col.Default.OriginString) > 0 { 108 nullOrNot = "DEFAULT " + col.Default.OriginString 109 } else if col.Default.NullAbility { 110 nullOrNot = "DEFAULT NULL" 111 } 112 113 if col.Typ.AutoIncr { 114 nullOrNot = "NOT NULL AUTO_INCREMENT" 115 } 116 117 var hasAttrComment string 118 if col.Comment != "" { 119 hasAttrComment = " COMMENT '" + col.Comment + "'" 120 } 121 122 if rowCount == 0 { 123 createStr += "\n" 124 } else { 125 createStr += ",\n" 126 } 127 typ := types.Type{Oid: types.T(col.Typ.Id)} 128 typeStr := typ.String() 129 if types.IsDecimal(typ.Oid) { //after decimal fix,remove this 130 typeStr = fmt.Sprintf("DECIMAL(%d,%d)", col.Typ.Width, col.Typ.Scale) 131 } 132 if typ.Oid == types.T_varchar || typ.Oid == types.T_char { 133 typeStr += fmt.Sprintf("(%d)", col.Typ.Width) 134 } 135 if types.IsFloat(typ.Oid) && col.Typ.Precision != -1 { 136 typeStr += fmt.Sprintf("(%d,%d)", col.Typ.Width, col.Typ.Precision) 137 } 138 139 updateOpt := "" 140 if col.OnUpdate != nil && col.OnUpdate.Expr != nil { 141 updateOpt = " ON UPDATE " + col.OnUpdate.OriginString 142 } 143 createStr += fmt.Sprintf("`%s` %s %s%s%s", colName, typeStr, nullOrNot, updateOpt, hasAttrComment) 144 rowCount++ 145 if col.Primary { 146 pkDefs = append(pkDefs, colName) 147 } 148 } 149 if tableDef.CompositePkey != nil { 150 pkDefs = append(pkDefs, util.SplitCompositePrimaryKeyColumnName(tableDef.CompositePkey.Name)...) 151 } 152 if len(pkDefs) != 0 { 153 pkStr := "PRIMARY KEY (" 154 for i, def := range pkDefs { 155 if i == len(pkDefs)-1 { 156 pkStr += fmt.Sprintf("`%s`", def) 157 } else { 158 pkStr += fmt.Sprintf("`%s`,", def) 159 } 160 } 161 pkStr += ")" 162 if rowCount != 0 { 163 createStr += ",\n" 164 } 165 createStr += pkStr 166 } 167 168 if tableDef.Indexes != nil { 169 for _, indexdef := range tableDef.Indexes { 170 var indexStr string 171 if indexdef.Unique { 172 indexStr = "UNIQUE KEY " 173 } else { 174 indexStr = "KEY " 175 } 176 indexStr += fmt.Sprintf("`%s` (", indexdef.IndexName) 177 for num, part := range indexdef.Parts { 178 if num == len(indexdef.Parts)-1 { 179 indexStr += fmt.Sprintf("`%s`", part) 180 } else { 181 indexStr += fmt.Sprintf("`%s`,", part) 182 } 183 } 184 indexStr += ")" 185 if indexdef.Comment != "" { 186 indexStr += fmt.Sprintf(" COMMENT `%s`", indexdef.Comment) 187 } 188 if rowCount != 0 { 189 createStr += ",\n" 190 } 191 createStr += indexStr 192 } 193 } 194 195 for _, fk := range tableDef.Fkeys { 196 colNames := make([]string, len(fk.Cols)) 197 for i, colId := range fk.Cols { 198 colNames[i] = colIdToName[colId] 199 } 200 _, fkTableDef := ctx.ResolveById(fk.ForeignTbl) 201 fkColIdToName := make(map[uint64]string) 202 for _, col := range fkTableDef.Cols { 203 fkColIdToName[col.ColId] = col.Name 204 } 205 fkColNames := make([]string, len(fk.ForeignCols)) 206 for i, colId := range fk.ForeignCols { 207 fkColNames[i] = fkColIdToName[colId] 208 } 209 210 if rowCount != 0 { 211 createStr += ",\n" 212 } 213 createStr += fmt.Sprintf("CONSTRAINT `%s` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) ON DELETE %s ON UPDATE %s", 214 fk.Name, strings.Join(colNames, "`,`"), fkTableDef.Name, strings.Join(fkColNames, "`,`"), fk.OnDelete.String(), fk.OnUpdate.String()) 215 } 216 217 if rowCount != 0 { 218 createStr += "\n" 219 } 220 createStr += ")" 221 222 if tableDef.ClusterBy != nil { 223 clusterby := " CLUSTER BY (" 224 if util.JudgeIsCompositeClusterByColumn(tableDef.ClusterBy.Name) { 225 //multi column clusterby 226 cbNames := util.SplitCompositeClusterByColumnName(tableDef.ClusterBy.Name) 227 for i, cbName := range cbNames { 228 if i != 0 { 229 clusterby += fmt.Sprintf(", `%s`", cbName) 230 } else { 231 clusterby += fmt.Sprintf("`%s`", cbName) 232 } 233 } 234 } else { 235 //single column cluster by 236 clusterby += fmt.Sprintf("`%s`", tableDef.ClusterBy.Name) 237 } 238 clusterby += ")" 239 createStr += clusterby 240 } 241 242 var comment string 243 var partition string 244 for _, def := range tableDef.Defs { 245 if proDef, ok := def.Def.(*plan.TableDef_DefType_Properties); ok { 246 for _, kv := range proDef.Properties.Properties { 247 if kv.Key == catalog.SystemRelAttr_Comment { 248 comment = " COMMENT='" + kv.Value + "'" 249 } 250 } 251 } 252 } 253 254 if tableDef.Partition != nil { 255 partition = ` ` + tableDef.Partition.PartitionMsg 256 } 257 258 createStr += comment 259 createStr += partition 260 261 if tableDef.TableType == catalog.SystemExternalRel { 262 param := tree.ExternParam{} 263 err := json.Unmarshal([]byte(tableDef.Createsql), ¶m) 264 if err != nil { 265 return nil, err 266 } 267 createStr += fmt.Sprintf(" INFILE{'FILEPATH'='%s','COMPRESSION'='%s','FORMAT'='%s','JSONDATA'='%s'}", param.Filepath, param.CompressType, param.Format, param.JsonData) 268 269 escapedby := "" 270 if param.Tail.Fields.EscapedBy != byte(0) { 271 escapedby = fmt.Sprintf(" ESCAPED BY '%c'", param.Tail.Fields.EscapedBy) 272 } 273 274 line := "" 275 if param.Tail.Lines.StartingBy != "" { 276 line = fmt.Sprintf(" LINE STARTING BY '%s'", param.Tail.Lines.StartingBy) 277 } 278 lineEnd := "" 279 if param.Tail.Lines.TerminatedBy == "\n" || param.Tail.Lines.TerminatedBy == "\r\n" { 280 lineEnd = " TERMINATED BY '\\\\n'" 281 } else { 282 lineEnd = fmt.Sprintf(" TERMINATED BY '%s'", param.Tail.Lines.TerminatedBy) 283 } 284 if len(line) > 0 { 285 line += lineEnd 286 } else { 287 line = " LINES" + lineEnd 288 } 289 290 createStr += fmt.Sprintf(" FIELDS TERMINATED BY '%s' ENCLOSED BY '%c'%s", param.Tail.Fields.Terminated, rune(param.Tail.Fields.EnclosedBy), escapedby) 291 createStr += line 292 if param.Tail.IgnoredLines > 0 { 293 createStr += fmt.Sprintf(" IGNORE %d LINES", param.Tail.IgnoredLines) 294 } 295 } 296 297 sql := "select \"%s\" as `Table`, \"%s\" as `Create Table`" 298 var buf bytes.Buffer 299 for _, ch := range createStr { 300 if ch == '"' { 301 buf.WriteRune('"') 302 } 303 buf.WriteRune(ch) 304 } 305 sql = fmt.Sprintf(sql, tblName, buf.String()) 306 307 return returnByRewriteSQL(ctx, sql, plan.DataDefinition_SHOW_CREATETABLE) 308 } 309 310 // buildShowCreateView 311 func buildShowCreateView(stmt *tree.ShowCreateView, ctx CompilerContext) (*Plan, error) { 312 tblName := stmt.Name.Parts[0] 313 dbName := ctx.DefaultDatabase() 314 if stmt.Name.NumParts == 2 { 315 dbName = stmt.Name.Parts[1] 316 } 317 318 _, tableDef := ctx.Resolve(dbName, tblName) 319 if tableDef == nil || tableDef.TableType != catalog.SystemViewRel { 320 return nil, moerr.NewInvalidInput(ctx.GetContext(), "show view '%s' is not a valid view", tblName) 321 } 322 sqlStr := "select \"%s\" as `View`, \"%s\" as `Create View`" 323 var viewStr string 324 if tableDef.TableType == catalog.SystemViewRel { 325 viewStr = tableDef.ViewSql.View 326 } 327 328 var viewData ViewData 329 err := json.Unmarshal([]byte(viewStr), &viewData) 330 if err != nil { 331 return nil, err 332 } 333 334 // FixMe We need a better escape function 335 stmtStr := strings.ReplaceAll(viewData.Stmt, "\"", "\\\"") 336 sqlStr = fmt.Sprintf(sqlStr, tblName, fmt.Sprint(stmtStr)) 337 338 // logutil.Info(sqlStr) 339 340 return returnByRewriteSQL(ctx, sqlStr, plan.DataDefinition_SHOW_CREATETABLE) 341 } 342 343 func buildShowDatabases(stmt *tree.ShowDatabases, ctx CompilerContext) (*Plan, error) { 344 if stmt.Like != nil && stmt.Where != nil { 345 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 346 } 347 348 accountId := ctx.GetAccountId() 349 ddlType := plan.DataDefinition_SHOW_DATABASES 350 351 var sql string 352 // Any account should shows database MO_CATALOG_DB_NAME 353 if accountId == catalog.System_Account { 354 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and datname = '%s')", accountId, MO_CATALOG_DB_NAME) 355 sql = fmt.Sprintf("SELECT datname `Database` FROM %s.mo_database where (%s)", MO_CATALOG_DB_NAME, accountClause) 356 } else { 357 sql = fmt.Sprintf("SELECT datname `Database` FROM %s.mo_database", MO_CATALOG_DB_NAME) 358 } 359 360 if stmt.Where != nil { 361 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 362 } 363 364 if stmt.Like != nil { 365 // append filter [AND datname like stmt.Like] to WHERE clause 366 likeExpr := stmt.Like 367 likeExpr.Left = tree.SetUnresolvedName("datname") 368 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 369 } 370 371 return returnByRewriteSQL(ctx, sql, ddlType) 372 } 373 374 func buildShowTables(stmt *tree.ShowTables, ctx CompilerContext) (*Plan, error) { 375 if stmt.Like != nil && stmt.Where != nil { 376 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 377 } 378 379 if stmt.Open { 380 return nil, moerr.NewNYI(ctx.GetContext(), "statement: '%v'", tree.String(stmt, dialect.MYSQL)) 381 } 382 383 accountId := ctx.GetAccountId() 384 dbName := stmt.DBName 385 if stmt.DBName == "" { 386 dbName = ctx.DefaultDatabase() 387 } else if !ctx.DatabaseExists(dbName) { 388 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 389 } 390 391 if dbName == "" { 392 return nil, moerr.NewNoDB(ctx.GetContext()) 393 } 394 ddlType := plan.DataDefinition_SHOW_TABLES 395 var tableType string 396 if stmt.Full { 397 tableType = fmt.Sprintf(", case relkind when 'v' then 'VIEW' when '%s' then 'CLUSTER TABLE' else 'BASE TABLE' end as Table_type", catalog.SystemClusterRel) 398 } 399 400 var sql string 401 if accountId == catalog.System_Account { 402 mustShowTable := "relname = 'mo_database' or relname = 'mo_tables' or relname = 'mo_columns'" 403 clusterTable := fmt.Sprintf(" or relkind = '%s'", catalog.SystemClusterRel) 404 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 405 sql = fmt.Sprintf("SELECT relname as `Tables_in_%s` %s FROM %s.mo_tables WHERE reldatabase = '%s' and relname != '%s' and relname not like '%s' and (%s)", 406 dbName, tableType, MO_CATALOG_DB_NAME, dbName, "%!%mo_increment_columns", "__mo_index_unique__%", accountClause) 407 } else { 408 sql = fmt.Sprintf("SELECT relname as `Tables_in_%s` %s FROM %s.mo_tables WHERE reldatabase = '%s' and relname != '%s' and relname not like '%s'", 409 dbName, tableType, MO_CATALOG_DB_NAME, dbName, "%!%mo_increment_columns", "__mo_index_unique__%") 410 } 411 412 if stmt.Where != nil { 413 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 414 } 415 416 if stmt.Like != nil { 417 // append filter [AND relname like stmt.Like] to WHERE clause 418 likeExpr := stmt.Like 419 likeExpr.Left = tree.SetUnresolvedName("relname") 420 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 421 } 422 423 return returnByRewriteSQL(ctx, sql, ddlType) 424 } 425 426 func buildShowTableNumber(stmt *tree.ShowTableNumber, ctx CompilerContext) (*Plan, error) { 427 accountId := ctx.GetAccountId() 428 dbName := stmt.DbName 429 if stmt.DbName == "" { 430 dbName = ctx.DefaultDatabase() 431 } else if !ctx.DatabaseExists(dbName) { 432 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 433 } 434 435 if dbName == "" { 436 return nil, moerr.NewNoDB(ctx.GetContext()) 437 } 438 439 ddlType := plan.DataDefinition_SHOW_TABLES 440 var sql string 441 442 if accountId == catalog.System_Account { 443 mustShowTable := "relname = 'mo_database' or relname = 'mo_tables' or relname = 'mo_columns'" 444 clusterTable := fmt.Sprintf(" or relkind = '%s'", catalog.SystemClusterRel) 445 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 446 sql = fmt.Sprintf("SELECT count(relname) `Number of tables in %s` FROM %s.mo_tables WHERE reldatabase = '%s' and relname != '%s' and relname not like '%s' and (%s)", 447 dbName, MO_CATALOG_DB_NAME, dbName, "%!%mo_increment_columns", "__mo_index_unique__%", accountClause) 448 } else { 449 sql = "SELECT count(relname) `Number of tables in %s` FROM %s.mo_tables WHERE reldatabase = '%s' and relname != '%s' and relname not like '%s'" 450 sql = fmt.Sprintf(sql, dbName, MO_CATALOG_DB_NAME, dbName, "%!%mo_increment_columns", "__mo_index_unique__%") 451 } 452 453 return returnByRewriteSQL(ctx, sql, ddlType) 454 } 455 456 func buildShowColumnNumber(stmt *tree.ShowColumnNumber, ctx CompilerContext) (*Plan, error) { 457 accountId := ctx.GetAccountId() 458 dbName := stmt.Table.GetDBName() 459 if dbName == "" { 460 dbName = ctx.DefaultDatabase() 461 } else if !ctx.DatabaseExists(dbName) { 462 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 463 } 464 465 tblName := string(stmt.Table.ToTableName().ObjectName) 466 _, tableDef := ctx.Resolve(dbName, tblName) 467 if tableDef == nil { 468 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 469 } 470 471 ddlType := plan.DataDefinition_SHOW_COLUMNS 472 var sql string 473 474 if accountId == catalog.System_Account { 475 mustShowTable := "att_relname = 'mo_database' or att_relname = 'mo_tables' or att_relname = 'mo_columns'" 476 clusterTable := "" 477 if util.TableIsClusterTable(tableDef.GetTableType()) { 478 clusterTable = fmt.Sprintf(" or att_relname = '%s'", tblName) 479 } 480 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 481 sql = "SELECT count(attname) `Number of columns in %s` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND (%s)" 482 sql = fmt.Sprintf(sql, tblName, MO_CATALOG_DB_NAME, dbName, tblName, accountClause) 483 } else { 484 sql = "SELECT count(attname) `Number of columns in %s` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s'" 485 sql = fmt.Sprintf(sql, tblName, MO_CATALOG_DB_NAME, dbName, tblName) 486 } 487 488 return returnByRewriteSQL(ctx, sql, ddlType) 489 } 490 491 func buildShowTableValues(stmt *tree.ShowTableValues, ctx CompilerContext) (*Plan, error) { 492 dbName := stmt.Table.GetDBName() 493 if dbName == "" { 494 dbName = ctx.DefaultDatabase() 495 } else if !ctx.DatabaseExists(dbName) { 496 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 497 } 498 499 tblName := string(stmt.Table.ToTableName().ObjectName) 500 _, tableDef := ctx.Resolve(dbName, tblName) 501 if tableDef == nil { 502 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 503 } 504 505 ddlType := plan.DataDefinition_SHOW_TARGET 506 507 sql := "SELECT" 508 tableCols := tableDef.Cols 509 isAllNull := true 510 for i := range tableCols { 511 colName := tableCols[i].Name 512 if types.T(tableCols[i].GetTyp().Id) == types.T_json { 513 sql += " null as `max(%s)`, null as `min(%s)`," 514 sql = fmt.Sprintf(sql, colName, colName) 515 } else { 516 sql += " max(%s), min(%s)," 517 sql = fmt.Sprintf(sql, colName, colName) 518 isAllNull = false 519 } 520 } 521 sql = sql[:len(sql)-1] 522 sql += " FROM %s" 523 524 if isAllNull { 525 sql += " LIMIT 1" 526 } 527 sql = fmt.Sprintf(sql, tblName) 528 529 return returnByRewriteSQL(ctx, sql, ddlType) 530 } 531 532 func buildShowColumns(stmt *tree.ShowColumns, ctx CompilerContext) (*Plan, error) { 533 if stmt.Like != nil && stmt.Where != nil { 534 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 535 } 536 537 accountId := ctx.GetAccountId() 538 dbName := stmt.Table.GetDBName() 539 if dbName == "" { 540 dbName = ctx.DefaultDatabase() 541 } else if !ctx.DatabaseExists(dbName) { 542 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 543 } 544 545 tblName := string(stmt.Table.ToTableName().ObjectName) 546 _, tableDef := ctx.Resolve(dbName, tblName) 547 if tableDef == nil { 548 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 549 } 550 551 var keyStr string 552 if dbName == catalog.MO_CATALOG && tblName == catalog.MO_DATABASE { 553 keyStr = "case when attname = '" + catalog.SystemDBAttr_ID + "' then 'PRI' else '' END as `Key`" 554 } else if dbName == catalog.MO_CATALOG && tblName == catalog.MO_TABLES { 555 keyStr = "case when attname = '" + catalog.SystemRelAttr_ID + "' then 'PRI' else '' END as `Key`" 556 } else if dbName == catalog.MO_CATALOG && tblName == catalog.MO_COLUMNS { 557 keyStr = "case when attname = '" + catalog.SystemColAttr_UniqName + "' then 'PRI' else '' END as `Key`" 558 } else { 559 if tableDef.Pkey != nil { 560 keyStr += "case" 561 for _, name := range tableDef.Pkey.Names { 562 keyStr += " when attname = " 563 keyStr += "'" + name + "'" 564 keyStr += " then 'PRI'" 565 } 566 keyStr += " else '' END as `Key`" 567 } else { 568 keyStr = "'' as `Key`" 569 } 570 } 571 572 ddlType := plan.DataDefinition_SHOW_COLUMNS 573 574 var sql string 575 if accountId == catalog.System_Account { 576 mustShowTable := "att_relname = 'mo_database' or att_relname = 'mo_tables' or att_relname = 'mo_columns'" 577 clusterTable := "" 578 if util.TableIsClusterTable(tableDef.GetTableType()) { 579 clusterTable = fmt.Sprintf(" or att_relname = '%s'", tblName) 580 } 581 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 582 sql = "SELECT attname `Field`, atttyp `Type`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, att_default `Default`, null `Extra`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND (%s)" 583 if stmt.Full { 584 sql = "SELECT attname `Field`, atttyp `Type`, null `Collation`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, att_default `Default`, null `Extra`,'select,insert,update,references' `Privileges`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND (%s)" 585 } 586 sql = fmt.Sprintf(sql, keyStr, MO_CATALOG_DB_NAME, dbName, tblName, accountClause) 587 } else { 588 sql = "SELECT attname `Field`, atttyp `Type`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, att_default `Default`, null `Extra`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s'" 589 if stmt.Full { 590 sql = "SELECT attname `Field`, atttyp `Type`, null `Collation`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, att_default `Default`, null `Extra`,'select,insert,update,references' `Privileges`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s'" 591 } 592 sql = fmt.Sprintf(sql, keyStr, MO_CATALOG_DB_NAME, dbName, tblName) 593 } 594 595 if stmt.Where != nil { 596 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 597 } 598 599 if stmt.Like != nil { 600 // append filter [AND ma.attname like stmt.Like] to WHERE clause 601 likeExpr := stmt.Like 602 likeExpr.Left = tree.SetUnresolvedName("attname") 603 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 604 } 605 606 return returnByRewriteSQL(ctx, sql, ddlType) 607 } 608 609 func buildShowTableStatus(stmt *tree.ShowTableStatus, ctx CompilerContext) (*Plan, error) { 610 if stmt.Like != nil && stmt.Where != nil { 611 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 612 } 613 614 dbName := stmt.DbName 615 if stmt.DbName == "" { 616 dbName = ctx.DefaultDatabase() 617 stmt.DbName = dbName 618 if dbName == "" { 619 return nil, moerr.NewNoDB(ctx.GetContext()) 620 } 621 } else if !ctx.DatabaseExists(dbName) { 622 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 623 } 624 625 ddlType := plan.DataDefinition_SHOW_TABLE_STATUS 626 accountId := ctx.GetAccountId() 627 mustShowTable := "relname = 'mo_database' or relname = 'mo_tables' or relname = 'mo_columns'" 628 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable) 629 sql := "select relname as `Name`, 'Tae' as `Engine`, 'Dynamic' as `Row_format`, 0 as `Rows`, 0 as `Avg_row_length`, 0 as `Data_length`, 0 as `Max_data_length`, 0 as `Index_length`, 'NULL' as `Data_free`, 0 as `Auto_increment`, created_time as `Create_time`, 'NULL' as `Update_time`, 'NULL' as `Check_time`, 'utf-8' as `Collation`, 'NULL' as `Checksum`, '' as `Create_options`, rel_comment as `Comment` from %s.mo_tables where reldatabase = '%s' and relname != '%s' and relname not like '%s' and (%s)" 630 631 sql = fmt.Sprintf(sql, MO_CATALOG_DB_NAME, dbName, "%!%mo_increment_columns", "__mo_index_unique__%", accountClause) 632 633 if stmt.Where != nil { 634 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 635 } 636 637 if stmt.Like != nil { 638 // append filter [AND ma.relname like stmt.Like] to WHERE clause 639 likeExpr := stmt.Like 640 likeExpr.Left = tree.SetUnresolvedName("relname") 641 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 642 } 643 644 return returnByRewriteSQL(ctx, sql, ddlType) 645 } 646 647 // TODO: Implement show target 648 func buildShowTarget(stmt *tree.ShowTarget, ctx CompilerContext) (*Plan, error) { 649 ddlType := plan.DataDefinition_SHOW_TARGET 650 sql := "" 651 switch stmt.Type { 652 case tree.ShowCharset: 653 sql = "select '' as `Charset`, '' as `Description`, '' as `Default collation`, '' as `Maxlen` where 0" 654 case tree.ShowTriggers: 655 return buildShowTriggers(stmt, ctx) 656 default: 657 sql = "select 1 where 0" 658 } 659 return returnByRewriteSQL(ctx, sql, ddlType) 660 } 661 662 func buildShowLocks(stmt *tree.ShowLocks, ctx CompilerContext) (*Plan, error) { 663 ddlType := plan.DataDefinition_SHOW_TARGET 664 sql := "select 1 where 0" 665 return returnByRewriteSQL(ctx, sql, ddlType) 666 } 667 668 func buildShowNodeList(stmt *tree.ShowNodeList, ctx CompilerContext) (*Plan, error) { 669 ddlType := plan.DataDefinition_SHOW_TARGET 670 sql := "select 1 where 0" 671 return returnByRewriteSQL(ctx, sql, ddlType) 672 } 673 674 func buildShowFunctionStatus(stmt *tree.ShowFunctionStatus, ctx CompilerContext) (*Plan, error) { 675 if stmt.Like != nil && stmt.Where != nil { 676 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 677 } 678 ddlType := plan.DataDefinition_SHOW_TARGET 679 if stmt.Like != nil && stmt.Where != nil { 680 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 681 } 682 683 sql := fmt.Sprintf("SELECT db as `Db`, name as `Name`, type as `Type`, definer as `Definer`, modified_time as `Modified`, created_time as `Created`, security_type as `Security_type`, comment as `Comment`, character_set_client, collation_connection, database_collation as `Database Collation` FROM %s.mo_user_defined_function", MO_CATALOG_DB_NAME) 684 685 if stmt.Where != nil { 686 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 687 } 688 689 if stmt.Like != nil { 690 // append filter [AND ma.attname like stmt.Like] to WHERE clause 691 likeExpr := stmt.Like 692 likeExpr.Left = tree.SetUnresolvedName("name") 693 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 694 } 695 696 return returnByRewriteSQL(ctx, sql, ddlType) 697 } 698 699 func buildShowTriggers(stmt *tree.ShowTarget, ctx CompilerContext) (*Plan, error) { 700 if stmt.Like != nil && stmt.Where != nil { 701 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 702 } 703 704 dbName := stmt.DbName 705 if stmt.DbName == "" { 706 dbName = ctx.DefaultDatabase() 707 stmt.DbName = dbName 708 if dbName == "" { 709 return nil, moerr.NewNoDB(ctx.GetContext()) 710 } 711 } else if !ctx.DatabaseExists(dbName) { 712 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 713 } 714 715 ddlType := plan.DataDefinition_SHOW_TARGET 716 sql := fmt.Sprintf("SELECT trigger_name as `Trigger`, event_manipulation as `Event`, event_object_table as `Table`, action_statement as `Statement`, action_timing as `Timing`, created as `Created`, sql_mode, definer as `Definer`, character_set_client, collation_connection, database_collation as `Database Collation` FROM %s.TRIGGERS ", INFORMATION_SCHEMA) 717 718 if stmt.Where != nil { 719 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 720 } 721 722 if stmt.Like != nil { 723 // append filter [AND ma.attname like stmt.Like] to WHERE clause 724 likeExpr := stmt.Like 725 likeExpr.Left = tree.SetUnresolvedName("event_object_table") 726 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 727 } 728 729 return returnByRewriteSQL(ctx, sql, ddlType) 730 } 731 732 func buildShowIndex(stmt *tree.ShowIndex, ctx CompilerContext) (*Plan, error) { 733 dbName := string(stmt.TableName.Schema()) 734 if dbName == "" { 735 dbName = ctx.DefaultDatabase() 736 if dbName == "" { 737 return nil, moerr.NewNoDB(ctx.GetContext()) 738 } 739 } else if !ctx.DatabaseExists(dbName) { 740 return nil, moerr.NewBadDB(ctx.GetContext(), dbName) 741 } 742 743 tblName := string(stmt.TableName.Name()) 744 _, tableDef := ctx.Resolve(dbName, tblName) 745 if tableDef == nil { 746 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 747 } 748 749 accountId := ctx.GetAccountId() 750 ddlType := plan.DataDefinition_SHOW_INDEX 751 mustShowTable := "att_relname = 'mo_database' or att_relname = 'mo_tables' or att_relname = 'mo_columns'" 752 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable) 753 sql := "select att_relname as `Table`, iff(att_constraint_type = 'p', 1, 0) as `Non_unique`, iff(att_constraint_type = 'p', 'PRIMARY', attname) as `Key_name`, 1 as `Seq_in_index`, attname as `Column_name`, 'A' as `Collation`, 0 as `Cardinality`, 'NULL' as `Sub_part`, 'NULL' as `Packed`, iff(attnotnull = 0, 'YES', 'NO') as `Null`, '' as 'Index_type', att_comment as `Comment`, iff(att_is_hidden = 0, 'YES', 'NO') as `Visible`, 'NULL' as `Expression` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND (%s)" 754 755 sql = fmt.Sprintf(sql, MO_CATALOG_DB_NAME, dbName, tblName, accountClause) 756 757 if stmt.Where != nil { 758 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 759 } 760 761 return returnByRewriteSQL(ctx, sql, ddlType) 762 } 763 764 // TODO: Improve SQL. Currently, Lack of the mata of grants 765 func buildShowGrants(stmt *tree.ShowGrants, ctx CompilerContext) (*Plan, error) { 766 767 ddlType := plan.DataDefinition_SHOW_TARGET 768 if stmt.ShowGrantType == tree.GrantForRole { 769 role_name := stmt.Roles[0].UserName 770 sql := "select concat(\"GRANT \", p.privilege_name, ' ON ', p.obj_type, ' ', case p.obj_type when 'account' then '' else p.privilege_level end, \" `%s`\") as `Grants for %s` from %s.mo_role_privs as p where p.role_name = '%s';" 771 sql = fmt.Sprintf(sql, role_name, role_name, MO_CATALOG_DB_NAME, role_name) 772 return returnByRewriteSQL(ctx, sql, ddlType) 773 } else { 774 if stmt.Hostname == "" { 775 stmt.Hostname = MO_DEFUALT_HOSTNAME 776 } 777 if stmt.Username == "" { 778 stmt.Username = ctx.GetUserName() 779 } 780 sql := "select concat(\"GRANT \", p.privilege_name, ' ON ', p.obj_type, ' ', case p.obj_type when 'account' then '' else p.privilege_level end, \" `%s`\", \"@\", \"`%s`\") as `Grants for %s@localhost` from mo_catalog.mo_user as u, mo_catalog.mo_role_privs as p, mo_catalog.mo_user_grant as g where g.role_id = p.role_id and g.user_id = u.user_id and u.user_name = '%s' and u.user_host = '%s';" 781 sql = fmt.Sprintf(sql, stmt.Username, stmt.Hostname, stmt.Username, stmt.Username, stmt.Hostname) 782 return returnByRewriteSQL(ctx, sql, ddlType) 783 } 784 } 785 786 func buildShowVariables(stmt *tree.ShowVariables, ctx CompilerContext) (*Plan, error) { 787 showVariables := &plan.ShowVariables{ 788 Global: stmt.Global, 789 } 790 791 // we deal with 'show vriables' statement in frontend now. 792 // so just return an empty plan in building plan for prepare statment is ok. 793 794 // if stmt.Like != nil && stmt.Where != nil { 795 // return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 796 // } 797 798 // builder := NewQueryBuilder(plan.Query_SELECT, ctx) 799 // binder := NewWhereBinder(builder, &BindContext{}) 800 801 // if stmt.Like != nil { 802 // // here will error because stmt.Like.Left is nil, you need add left expr like : stmt.Like.Left = tree.SetUnresolvedName("column_name") 803 // // but we have no column name, because Variables is save in a hashmap in frontend, not a table. 804 // expr, err := binder.bindComparisonExpr(stmt.Like, 0, false) 805 // if err != nil { 806 // return nil, err 807 // } 808 // showVariables.Where = append(showVariables.Where, expr) 809 // } 810 // if stmt.Where != nil { 811 // exprs, err := splitAndBindCondition(stmt.Where.Expr, &BindContext{}) 812 // if err != nil { 813 // return nil, err 814 // } 815 // showVariables.Where = append(showVariables.Where, exprs...) 816 // } 817 818 return &Plan{ 819 Plan: &plan.Plan_Ddl{ 820 Ddl: &plan.DataDefinition{ 821 DdlType: plan.DataDefinition_SHOW_VARIABLES, 822 Definition: &plan.DataDefinition_ShowVariables{ 823 ShowVariables: showVariables, 824 }, 825 }, 826 }, 827 }, nil 828 } 829 830 func buildShowStatus(stmt *tree.ShowStatus, ctx CompilerContext) (*Plan, error) { 831 ddlType := plan.DataDefinition_SHOW_STATUS 832 sql := "select '' as `Variable_name`, '' as `Value` where 0" 833 return returnByRewriteSQL(ctx, sql, ddlType) 834 } 835 836 func buildShowCollation(stmt *tree.ShowCollation, ctx CompilerContext) (*Plan, error) { 837 ddlType := plan.DataDefinition_SHOW_COLLATION 838 sql := "select 'utf8mb4_bin' as `Collation`, 'utf8mb4' as `Charset`, 46 as `Id`, 'Yes' as `Compiled`, 1 as `Sortlen`" 839 return returnByRewriteSQL(ctx, sql, ddlType) 840 } 841 842 func buildShowProcessList(stmt *tree.ShowProcessList, ctx CompilerContext) (*Plan, error) { 843 ddlType := plan.DataDefinition_SHOW_PROCESSLIST 844 sql := "select '' as `Id`, '' as `User`, '' as `Host`, '' as `db` , '' as `Command`, '' as `Time` , '' as `State`, '' as `Info` where 0" 845 return returnByRewriteSQL(ctx, sql, ddlType) 846 } 847 848 func returnByRewriteSQL(ctx CompilerContext, sql string, 849 ddlType plan.DataDefinition_DdlType) (*Plan, error) { 850 stmt, err := getRewriteSQLStmt(ctx, sql) 851 if err != nil { 852 return nil, err 853 } 854 return getReturnDdlBySelectStmt(ctx, stmt, ddlType) 855 } 856 857 func returnByWhereAndBaseSQL(ctx CompilerContext, baseSQL string, 858 where *tree.Where, ddlType plan.DataDefinition_DdlType) (*Plan, error) { 859 sql := fmt.Sprintf("SELECT * FROM (%s) tbl", baseSQL) 860 // logutil.Info(sql) 861 newStmt, err := getRewriteSQLStmt(ctx, sql) 862 if err != nil { 863 return nil, err 864 } 865 // set show statement's where clause to new statement 866 newStmt.(*tree.Select).Select.(*tree.SelectClause).Where = where 867 return getReturnDdlBySelectStmt(ctx, newStmt, ddlType) 868 } 869 870 func returnByLikeAndSQL(ctx CompilerContext, sql string, like *tree.ComparisonExpr, 871 ddlType plan.DataDefinition_DdlType) (*Plan, error) { 872 newStmt, err := getRewriteSQLStmt(ctx, sql) 873 if err != nil { 874 return nil, err 875 } 876 var whereExpr *tree.Where 877 878 if newStmt.(*tree.Select).Select.(*tree.SelectClause).Where == nil { 879 whereExpr = &tree.Where{ 880 Type: "where", 881 Expr: like, 882 } 883 } else { 884 whereExpr = &tree.Where{ 885 Type: "where", 886 Expr: &tree.AndExpr{ 887 Left: newStmt.(*tree.Select).Select.(*tree.SelectClause).Where.Expr, 888 Right: like, 889 }, 890 } 891 } 892 // set show statement's like clause to new statement 893 newStmt.(*tree.Select).Select.(*tree.SelectClause).Where = whereExpr 894 // logutil.Info(tree.String(newStmt, dialect.MYSQL)) 895 return getReturnDdlBySelectStmt(ctx, newStmt, ddlType) 896 } 897 898 func getRewriteSQLStmt(ctx CompilerContext, sql string) (tree.Statement, error) { 899 newStmts, err := parsers.Parse(ctx.GetContext(), dialect.MYSQL, sql) 900 if err != nil { 901 return nil, err 902 } 903 if len(newStmts) != 1 { 904 return nil, moerr.NewInvalidInput(ctx.GetContext(), "rewrite can only contain one statement, %d provided", len(newStmts)) 905 } 906 return newStmts[0], nil 907 } 908 909 func getReturnDdlBySelectStmt(ctx CompilerContext, stmt tree.Statement, 910 ddlType plan.DataDefinition_DdlType) (*Plan, error) { 911 queryPlan, err := BuildPlan(ctx, stmt) 912 if err != nil { 913 return nil, err 914 } 915 return queryPlan, nil 916 // return &Plan{ 917 // Plan: &plan.Plan_Ddl{ 918 // Ddl: &plan.DataDefinition{ 919 // DdlType: ddlType, 920 // Query: queryPlan.GetQuery(), 921 // }, 922 // }, 923 // }, nil 924 }