github.com/matrixorigin/matrixone@v1.2.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 "go/constant" 22 "strings" 23 24 "github.com/matrixorigin/matrixone/pkg/catalog" 25 "github.com/matrixorigin/matrixone/pkg/common/moerr" 26 "github.com/matrixorigin/matrixone/pkg/container/types" 27 "github.com/matrixorigin/matrixone/pkg/pb/plan" 28 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 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 "github.com/matrixorigin/matrixone/pkg/sql/util" 33 ) 34 35 const MO_CATALOG_DB_NAME = "mo_catalog" 36 const MO_DEFUALT_HOSTNAME = "localhost" 37 const INFORMATION_SCHEMA = "information_schema" 38 39 func buildShowCreateDatabase(stmt *tree.ShowCreateDatabase, 40 ctx CompilerContext) (*Plan, error) { 41 var err error 42 var name string 43 // snapshot to fix 44 name, err = databaseIsValid(getSuitableDBName("", stmt.Name), ctx, Snapshot{TS: ×tamp.Timestamp{}}) 45 if err != nil { 46 return nil, err 47 } 48 49 if sub, err := ctx.GetSubscriptionMeta(name, Snapshot{TS: ×tamp.Timestamp{}}); err != nil { 50 return nil, err 51 } else if sub != nil { 52 accountId, err := ctx.GetAccountId() 53 if err != nil { 54 return nil, err 55 } 56 // get data from schema 57 //sql := fmt.Sprintf("SELECT md.datname as `Database` FROM %s.mo_database md WHERE md.datname = '%s'", MO_CATALOG_DB_NAME, stmt.Name) 58 sql := fmt.Sprintf("SELECT md.datname as `Database`,dat_createsql as `Create Database` FROM %s.mo_database md WHERE md.datname = '%s' and account_id=%d", MO_CATALOG_DB_NAME, stmt.Name, accountId) 59 return returnByRewriteSQL(ctx, sql, plan.DataDefinition_SHOW_CREATEDATABASE) 60 } 61 62 sqlStr := "select \"%s\" as `Database`, \"%s\" as `Create Database`" 63 createSql := fmt.Sprintf("CREATE DATABASE `%s`", name) 64 sqlStr = fmt.Sprintf(sqlStr, name, createSql) 65 66 return returnByRewriteSQL(ctx, sqlStr, plan.DataDefinition_SHOW_CREATEDATABASE) 67 } 68 69 func formatStr(str string) string { 70 tmp := strings.Replace(str, "`", "``", -1) 71 strLen := len(tmp) 72 if strLen < 2 { 73 return tmp 74 } 75 if tmp[0] == '\'' && tmp[strLen-1] == '\'' { 76 return "'" + strings.Replace(tmp[1:strLen-1], "'", "''", -1) + "'" 77 } 78 return strings.Replace(tmp, "'", "''", -1) 79 } 80 81 func buildShowCreateTable(stmt *tree.ShowCreateTable, ctx CompilerContext) (*Plan, error) { 82 var err error 83 tblName := stmt.Name.GetTableName() 84 dbName := stmt.Name.GetDBName() 85 86 snapshot := &Snapshot{TS: ×tamp.Timestamp{}} 87 if len(stmt.SnapshotName) > 0 { 88 if snapshot, err = ctx.ResolveSnapshotWithSnapshotName(stmt.SnapshotName); err != nil { 89 return nil, err 90 } 91 } 92 93 dbName, err = databaseIsValid(getSuitableDBName(dbName, ""), ctx, *snapshot) 94 if err != nil { 95 return nil, err 96 } 97 98 // check if the database is a subscription 99 sub, err := ctx.GetSubscriptionMeta(dbName, *snapshot) 100 if err != nil { 101 return nil, err 102 } 103 104 if sub != nil { 105 ctx.SetQueryingSubscription(sub) 106 defer func() { 107 ctx.SetQueryingSubscription(nil) 108 }() 109 } 110 111 _, tableDef := ctx.Resolve(dbName, tblName, *snapshot) 112 if tableDef == nil { 113 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 114 } 115 if tableDef.TableType == catalog.SystemViewRel { 116 var newStmt *tree.ShowCreateView 117 if stmt.Name.NumParts == 1 { 118 newStmt = tree.NewShowCreateView(tree.SetUnresolvedObjectName(1, [3]string{tblName, "", ""})) 119 } else if stmt.Name.NumParts == 2 { 120 newStmt = tree.NewShowCreateView(tree.SetUnresolvedObjectName(2, [3]string{tblName, dbName, ""})) 121 } 122 if len(stmt.SnapshotName) > 0 { 123 newStmt.SnapshotName = stmt.SnapshotName 124 } 125 126 return buildShowCreateView(newStmt, ctx) 127 } 128 129 // sql := ` 130 // SELECT * 131 // FROM %s.mo_tables mt JOIN %s.mo_columns mc 132 // ON mt.relname = mc.att_relname and mt.reldatabase=mc.att_database 133 // WHERE mt.reldatabase = '%s' AND mt.relname = '%s' 134 // ` 135 // sql = fmt.Sprintf(sql, MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, dbName, tblName) 136 // logutil.Info(sql) 137 138 var createStr string 139 if tableDef.TableType == catalog.SystemOrdinaryRel { 140 createStr = fmt.Sprintf("CREATE TABLE `%s` (", formatStr(tblName)) 141 } else if tableDef.TableType == catalog.SystemExternalRel { 142 createStr = fmt.Sprintf("CREATE EXTERNAL TABLE `%s` (", formatStr(tblName)) 143 } else if tableDef.TableType == catalog.SystemClusterRel { 144 createStr = fmt.Sprintf("CREATE CLUSTER TABLE `%s` (", formatStr(tblName)) 145 } else if tblName == catalog.MO_DATABASE || tblName == catalog.MO_TABLES || tblName == catalog.MO_COLUMNS { 146 createStr = fmt.Sprintf("CREATE TABLE `%s` (", formatStr(tblName)) 147 } 148 149 rowCount := 0 150 var pkDefs []string 151 isClusterTable := util.TableIsClusterTable(tableDef.TableType) 152 153 colIdToName := make(map[uint64]string) 154 for _, col := range tableDef.Cols { 155 if col.Hidden { 156 continue 157 } 158 colName := col.Name 159 colIdToName[col.ColId] = col.Name 160 if colName == catalog.Row_ID { 161 continue 162 } 163 //the non-sys account skips the column account_id of the cluster table 164 accountId, err := ctx.GetAccountId() 165 if err != nil { 166 return nil, err 167 } 168 if util.IsClusterTableAttribute(colName) && 169 isClusterTable && 170 accountId != catalog.System_Account { 171 continue 172 } 173 nullOrNot := "NOT NULL" 174 // col.Default must be not nil 175 if len(col.Default.OriginString) > 0 { 176 if !col.Primary { 177 nullOrNot = "DEFAULT " + formatStr(col.Default.OriginString) 178 } 179 } else if col.Default.NullAbility { 180 nullOrNot = "DEFAULT NULL" 181 } 182 183 if col.Typ.AutoIncr { 184 nullOrNot = "NOT NULL AUTO_INCREMENT" 185 } 186 187 var hasAttrComment string 188 if col.Comment != "" { 189 hasAttrComment = " COMMENT '" + col.Comment + "'" 190 } 191 192 if rowCount == 0 { 193 createStr += "\n" 194 } else { 195 createStr += ",\n" 196 } 197 typ := types.T(col.Typ.Id).ToType() 198 typeStr := typ.String() 199 if typ.Oid.IsDecimal() { //after decimal fix,remove this 200 typeStr = fmt.Sprintf("DECIMAL(%d,%d)", col.Typ.Width, col.Typ.Scale) 201 } 202 if typ.Oid == types.T_varchar || typ.Oid == types.T_char || 203 typ.Oid == types.T_binary || typ.Oid == types.T_varbinary || 204 typ.Oid.IsArrayRelate() || typ.Oid == types.T_bit { 205 typeStr += fmt.Sprintf("(%d)", col.Typ.Width) 206 } 207 if typ.Oid.IsFloat() && col.Typ.Scale != -1 { 208 typeStr += fmt.Sprintf("(%d,%d)", col.Typ.Width, col.Typ.Scale) 209 } 210 211 if typ.Oid.IsEnum() { 212 enums := strings.Split(col.Typ.GetEnumvalues(), ",") 213 typeStr += "(" 214 for i, enum := range enums { 215 typeStr += fmt.Sprintf("'%s'", enum) 216 if i < len(enums)-1 { 217 typeStr += "," 218 } 219 } 220 typeStr += ")" 221 } 222 223 updateOpt := "" 224 if col.OnUpdate != nil && col.OnUpdate.Expr != nil { 225 updateOpt = " ON UPDATE " + col.OnUpdate.OriginString 226 } 227 createStr += fmt.Sprintf("`%s` %s %s%s%s", formatStr(colName), typeStr, nullOrNot, updateOpt, hasAttrComment) 228 rowCount++ 229 if col.Primary { 230 pkDefs = append(pkDefs, colName) 231 } 232 } 233 234 // If it is a composite primary key, get the component columns of the composite primary key 235 if tableDef.Pkey != nil && len(tableDef.Pkey.Names) > 1 { 236 pkDefs = append(pkDefs, tableDef.Pkey.Names...) 237 } 238 239 if len(pkDefs) != 0 { 240 pkStr := "PRIMARY KEY (" 241 for i, def := range pkDefs { 242 if i == len(pkDefs)-1 { 243 pkStr += fmt.Sprintf("`%s`", formatStr(def)) 244 } else { 245 pkStr += fmt.Sprintf("`%s`,", formatStr(def)) 246 } 247 } 248 pkStr += ")" 249 if rowCount != 0 { 250 createStr += ",\n" 251 } 252 createStr += pkStr 253 } 254 255 if tableDef.Indexes != nil { 256 257 // We only print distinct index names. This is used to avoid printing the same index multiple times for IVFFLAT or 258 // other multi-table indexes. 259 indexNames := make(map[string]bool) 260 261 for _, indexdef := range tableDef.Indexes { 262 if _, ok := indexNames[indexdef.IndexName]; ok { 263 continue 264 } else { 265 indexNames[indexdef.IndexName] = true 266 } 267 268 var indexStr string 269 if indexdef.Unique { 270 indexStr = "UNIQUE KEY " 271 } else { 272 indexStr = "KEY " 273 } 274 indexStr += fmt.Sprintf("`%s` ", formatStr(indexdef.IndexName)) 275 if !catalog.IsNullIndexAlgo(indexdef.IndexAlgo) { 276 indexStr += fmt.Sprintf("USING %s ", indexdef.IndexAlgo) 277 } 278 indexStr += "(" 279 i := 0 280 for _, part := range indexdef.Parts { 281 if catalog.IsAlias(part) { 282 continue 283 } 284 if i > 0 { 285 indexStr += "," 286 } 287 288 indexStr += fmt.Sprintf("`%s`", formatStr(part)) 289 i++ 290 } 291 292 indexStr += ")" 293 if indexdef.IndexAlgoParams != "" { 294 var paramList string 295 paramList, err = catalog.IndexParamsToStringList(indexdef.IndexAlgoParams) 296 if err != nil { 297 return nil, err 298 } 299 indexStr += paramList 300 } 301 if indexdef.Comment != "" { 302 indexdef.Comment = strings.Replace(indexdef.Comment, "'", "\\'", -1) 303 indexStr += fmt.Sprintf(" COMMENT '%s'", formatStr(indexdef.Comment)) 304 } 305 if rowCount != 0 { 306 createStr += ",\n" 307 } 308 createStr += indexStr 309 } 310 } 311 312 for _, fk := range tableDef.Fkeys { 313 colNames := make([]string, len(fk.Cols)) 314 for i, colId := range fk.Cols { 315 colNames[i] = colIdToName[colId] 316 } 317 318 var fkTableDef *TableDef 319 320 //fk self reference 321 if fk.ForeignTbl == 0 { 322 fkTableDef = tableDef 323 } else { 324 if ctx.GetQueryingSubscription() != nil { 325 _, fkTableDef = ctx.ResolveSubscriptionTableById(fk.ForeignTbl, ctx.GetQueryingSubscription()) 326 } else { 327 _, fkTableDef = ctx.ResolveById(fk.ForeignTbl, *snapshot) 328 } 329 } 330 331 fkColIdToName := make(map[uint64]string) 332 for _, col := range fkTableDef.Cols { 333 fkColIdToName[col.ColId] = col.Name 334 } 335 fkColNames := make([]string, len(fk.ForeignCols)) 336 for i, colId := range fk.ForeignCols { 337 fkColNames[i] = fkColIdToName[colId] 338 } 339 340 if rowCount != 0 { 341 createStr += ",\n" 342 } 343 createStr += fmt.Sprintf("CONSTRAINT `%s` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) ON DELETE %s ON UPDATE %s", 344 formatStr(fk.Name), strings.Join(colNames, "`,`"), formatStr(fkTableDef.Name), strings.Join(fkColNames, "`,`"), fk.OnDelete.String(), fk.OnUpdate.String()) 345 } 346 347 if rowCount != 0 { 348 createStr += "\n" 349 } 350 createStr += ")" 351 352 var comment string 353 var partition string 354 for _, def := range tableDef.Defs { 355 if proDef, ok := def.Def.(*plan.TableDef_DefType_Properties); ok { 356 for _, kv := range proDef.Properties.Properties { 357 if kv.Key == catalog.SystemRelAttr_Comment { 358 comment = " COMMENT='" + kv.Value + "'" 359 } 360 } 361 } 362 } 363 364 if tableDef.Partition != nil { 365 partition = ` ` + tableDef.Partition.PartitionMsg 366 } 367 368 createStr += comment 369 createStr += partition 370 371 /** 372 Fix issue: https://github.com/matrixorigin/MO-Cloud/issues/1028#issuecomment-1667642384 373 Based on the grammar of the 'create table' in the file pkg/sql/parsers/dialect/mysql/mysql_sql.y 374 https://github.com/matrixorigin/matrixone/blob/68db7260e411e5a4541eaccf78ca9bb57e810f24/pkg/sql/parsers/dialect/mysql/mysql_sql.y#L6076C7-L6076C7 375 https://github.com/matrixorigin/matrixone/blob/68db7260e411e5a4541eaccf78ca9bb57e810f24/pkg/sql/parsers/dialect/mysql/mysql_sql.y#L6097 376 The 'cluster by' is after the 'partition by' and the 'table options', so we need to add the 'cluster by' string after the 'partition by' and the 'table options'. 377 */ 378 if tableDef.ClusterBy != nil { 379 clusterby := " CLUSTER BY (" 380 if util.JudgeIsCompositeClusterByColumn(tableDef.ClusterBy.Name) { 381 //multi column clusterby 382 cbNames := util.SplitCompositeClusterByColumnName(tableDef.ClusterBy.Name) 383 for i, cbName := range cbNames { 384 if i != 0 { 385 clusterby += fmt.Sprintf(", `%s`", formatStr(cbName)) 386 } else { 387 clusterby += fmt.Sprintf("`%s`", formatStr(cbName)) 388 } 389 } 390 } else { 391 //single column cluster by 392 clusterby += fmt.Sprintf("`%s`", formatStr(tableDef.ClusterBy.Name)) 393 } 394 clusterby += ")" 395 createStr += clusterby 396 } 397 398 if tableDef.TableType == catalog.SystemExternalRel { 399 param := tree.ExternParam{} 400 err := json.Unmarshal([]byte(tableDef.Createsql), ¶m) 401 if err != nil { 402 return nil, err 403 } 404 createStr += fmt.Sprintf(" INFILE{'FILEPATH'='%s','COMPRESSION'='%s','FORMAT'='%s','JSONDATA'='%s'}", param.Filepath, param.CompressType, param.Format, param.JsonData) 405 406 fields := "" 407 if param.Tail.Fields.Terminated != nil { 408 if param.Tail.Fields.Terminated.Value == "" { 409 fields += " TERMINATED BY \"\"" 410 } else { 411 fields += fmt.Sprintf(" TERMINATED BY '%s'", param.Tail.Fields.Terminated.Value) 412 } 413 } 414 if param.Tail.Fields.EnclosedBy != nil { 415 if param.Tail.Fields.EnclosedBy.Value == byte(0) { 416 fields += " ENCLOSED BY ''" 417 } else if param.Tail.Fields.EnclosedBy.Value == byte('\\') { 418 fields += " ENCLOSED BY '\\\\'" 419 } else { 420 fields += fmt.Sprintf(" ENCLOSED BY '%c'", param.Tail.Fields.EnclosedBy.Value) 421 } 422 } 423 if param.Tail.Fields.EscapedBy != nil { 424 if param.Tail.Fields.EscapedBy.Value == byte(0) { 425 fields += " ESCAPED BY ''" 426 } else if param.Tail.Fields.EscapedBy.Value == byte('\\') { 427 fields += " ESCAPED BY '\\\\'" 428 } else { 429 fields += fmt.Sprintf(" ESCAPED BY '%c'", param.Tail.Fields.EscapedBy.Value) 430 } 431 } 432 433 line := "" 434 if param.Tail.Lines.StartingBy != "" { 435 line += fmt.Sprintf(" STARTING BY '%s'", param.Tail.Lines.StartingBy) 436 } 437 if param.Tail.Lines.TerminatedBy != nil { 438 if param.Tail.Lines.TerminatedBy.Value == "\n" || param.Tail.Lines.TerminatedBy.Value == "\r\n" { 439 line += " TERMINATED BY '\\\\n'" 440 } else { 441 line += fmt.Sprintf(" TERMINATED BY '%s'", param.Tail.Lines.TerminatedBy) 442 } 443 } 444 445 if len(fields) > 0 { 446 fields = " FIELDS" + fields 447 createStr += fields 448 } 449 if len(line) > 0 { 450 line = " LINES" + line 451 createStr += line 452 } 453 454 if param.Tail.IgnoredLines > 0 { 455 createStr += fmt.Sprintf(" IGNORE %d LINES", param.Tail.IgnoredLines) 456 } 457 } 458 459 sql := "select \"%s\" as `Table`, \"%s\" as `Create Table`" 460 var buf bytes.Buffer 461 for _, ch := range createStr { 462 if ch == '"' { 463 buf.WriteRune('"') 464 } 465 buf.WriteRune(ch) 466 } 467 sql = fmt.Sprintf(sql, tblName, buf.String()) 468 469 return returnByRewriteSQL(ctx, sql, plan.DataDefinition_SHOW_CREATETABLE) 470 } 471 472 // buildShowCreateView 473 func buildShowCreateView(stmt *tree.ShowCreateView, ctx CompilerContext) (*Plan, error) { 474 var err error 475 tblName := stmt.Name.GetTableName() 476 dbName := stmt.Name.GetDBName() 477 478 snapshot := &Snapshot{TS: ×tamp.Timestamp{}} 479 if len(stmt.SnapshotName) > 0 { 480 if snapshot, err = ctx.ResolveSnapshotWithSnapshotName(stmt.SnapshotName); err != nil { 481 return nil, err 482 } 483 } 484 485 dbName, err = databaseIsValid(getSuitableDBName(dbName, ""), ctx, *snapshot) 486 if err != nil { 487 return nil, err 488 } 489 490 _, tableDef := ctx.Resolve(dbName, tblName, *snapshot) 491 if tableDef == nil || tableDef.TableType != catalog.SystemViewRel { 492 return nil, moerr.NewInvalidInput(ctx.GetContext(), "show view '%s' is not a valid view", tblName) 493 } 494 sqlStr := "select \"%s\" as `View`, \"%s\" as `Create View`, 'utf8mb4' as `character_set_client`, 'utf8mb4_general_ci' as `collation_connection`" 495 var viewStr string 496 if tableDef.TableType == catalog.SystemViewRel { 497 viewStr = tableDef.ViewSql.View 498 } 499 500 var viewData ViewData 501 err = json.Unmarshal([]byte(viewStr), &viewData) 502 if err != nil { 503 return nil, err 504 } 505 506 // FixMe We need a better escape function 507 stmtStr := strings.ReplaceAll(viewData.Stmt, "\"", "\\\"") 508 sqlStr = fmt.Sprintf(sqlStr, tblName, fmt.Sprint(stmtStr)) 509 510 // logutil.Info(sqlStr) 511 512 return returnByRewriteSQL(ctx, sqlStr, plan.DataDefinition_SHOW_CREATETABLE) 513 } 514 515 func buildShowDatabases(stmt *tree.ShowDatabases, ctx CompilerContext) (*Plan, error) { 516 if stmt.Like != nil && stmt.Where != nil { 517 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 518 } 519 520 accountId, err := ctx.GetAccountId() 521 if err != nil { 522 return nil, err 523 } 524 ddlType := plan.DataDefinition_SHOW_DATABASES 525 526 var sql string 527 snapshotSpec := "" 528 if len(stmt.SnapshotName) > 0 { 529 snapshot, err := ctx.ResolveSnapshotWithSnapshotName(stmt.SnapshotName) 530 if err != nil { 531 return nil, err 532 } 533 accountId = snapshot.Tenant.TenantID 534 snapshotSpec = fmt.Sprintf("{snapshot = '%s'}", stmt.SnapshotName) 535 } 536 // Any account should show database MO_CATALOG_DB_NAME 537 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and datname = '%s')", accountId, MO_CATALOG_DB_NAME) 538 sql = fmt.Sprintf("SELECT datname `Database` FROM %s.mo_database %s where (%s) ORDER BY %s", MO_CATALOG_DB_NAME, snapshotSpec, accountClause, catalog.SystemDBAttr_Name) 539 540 if stmt.Where != nil { 541 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 542 } 543 544 if stmt.Like != nil { 545 // append filter [AND datname like stmt.Like] to WHERE clause 546 likeExpr := stmt.Like 547 likeExpr.Left = tree.SetUnresolvedName("datname") 548 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 549 } 550 551 return returnByRewriteSQL(ctx, sql, ddlType) 552 } 553 554 func buildShowSequences(stmt *tree.ShowSequences, ctx CompilerContext) (*Plan, error) { 555 // snapshot to fix 556 dbName, err := databaseIsValid(stmt.DBName, ctx, Snapshot{TS: ×tamp.Timestamp{}}) 557 if err != nil { 558 return nil, err 559 } 560 561 ddlType := plan.DataDefinition_SHOW_SEQUENCES 562 563 sql := fmt.Sprintf("select %s.mo_tables.relname as `Names`, mo_show_visible_bin(%s.mo_columns.atttyp, 2) as 'Data Type' from %s.mo_tables left join %s.mo_columns on %s.mo_tables.rel_id = %s.mo_columns.att_relname_id where %s.mo_tables.relkind = '%s' and %s.mo_tables.reldatabase = '%s' and %s.mo_columns.attname = '%s'", MO_CATALOG_DB_NAME, 564 MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, catalog.SystemSequenceRel, MO_CATALOG_DB_NAME, dbName, MO_CATALOG_DB_NAME, Sequence_cols_name[0]) 565 566 if stmt.Where != nil { 567 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 568 } 569 570 return returnByRewriteSQL(ctx, sql, ddlType) 571 } 572 573 func buildShowTables(stmt *tree.ShowTables, ctx CompilerContext) (*Plan, error) { 574 if stmt.Like != nil && stmt.Where != nil { 575 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 576 } 577 578 if stmt.Open { 579 return nil, moerr.NewNYI(ctx.GetContext(), "statement: '%v'", tree.String(stmt, dialect.MYSQL)) 580 } 581 582 accountId, err := ctx.GetAccountId() 583 if err != nil { 584 return nil, err 585 } 586 587 snapshot := &Snapshot{TS: ×tamp.Timestamp{}} 588 snapshotSpec := "" 589 if len(stmt.SnapshotName) > 0 { 590 if snapshot, err = ctx.ResolveSnapshotWithSnapshotName(stmt.SnapshotName); err != nil { 591 return nil, err 592 } 593 accountId = snapshot.Tenant.TenantID 594 snapshotSpec = fmt.Sprintf("{snapshot = '%s'}", stmt.SnapshotName) 595 } 596 597 dbName, err := databaseIsValid(stmt.DBName, ctx, *snapshot) 598 if err != nil { 599 return nil, err 600 } 601 602 ddlType := plan.DataDefinition_SHOW_TABLES 603 var tableType string 604 if stmt.Full { 605 tableType = fmt.Sprintf(", case relkind when 'v' then 'VIEW' when '%s' then 'CLUSTER TABLE' else 'BASE TABLE' end as Table_type", catalog.SystemClusterRel) 606 } 607 608 sub, err := ctx.GetSubscriptionMeta(dbName, *snapshot) 609 if err != nil { 610 return nil, err 611 } 612 subName := dbName 613 if sub != nil { 614 accountId = uint32(sub.AccountId) 615 dbName = sub.DbName 616 ctx.SetQueryingSubscription(sub) 617 defer func() { 618 ctx.SetQueryingSubscription(nil) 619 }() 620 } 621 622 var sql string 623 mustShowTable := "relname = 'mo_database' or relname = 'mo_tables' or relname = 'mo_columns'" 624 clusterTable := fmt.Sprintf(" or relkind = '%s'", catalog.SystemClusterRel) 625 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 626 sql = fmt.Sprintf("SELECT relname as `Tables_in_%s` %s FROM %s.mo_tables %s WHERE reldatabase = '%s' and relname != '%s' and relname not like '%s' and relkind != '%s' and (%s)", 627 subName, tableType, MO_CATALOG_DB_NAME, snapshotSpec, dbName, catalog.MOAutoIncrTable, catalog.IndexTableNamePrefix+"%", catalog.SystemPartitionRel, accountClause) 628 629 // Do not show views in sub-db 630 if sub != nil { 631 sql += fmt.Sprintf(" and relkind != '%s'", catalog.SystemViewRel) 632 } 633 634 // Do not show sequences. 635 sql += fmt.Sprintf(" and relkind != '%s'", catalog.SystemSequenceRel) 636 637 // Order by relname 638 sql += fmt.Sprintf(" ORDER BY %s", catalog.SystemRelAttr_Name) 639 640 if stmt.Where != nil { 641 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 642 } 643 644 if stmt.Like != nil { 645 // append filter [AND relname like stmt.Like] to WHERE clause 646 likeExpr := stmt.Like 647 likeExpr.Left = tree.SetUnresolvedName("relname") 648 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 649 } 650 651 return returnByRewriteSQL(ctx, sql, ddlType) 652 } 653 654 func buildShowTableNumber(stmt *tree.ShowTableNumber, ctx CompilerContext) (*Plan, error) { 655 accountId, err := ctx.GetAccountId() 656 if err != nil { 657 return nil, err 658 } 659 // snapshot to fix 660 dbName, err := databaseIsValid(stmt.DbName, ctx, Snapshot{TS: ×tamp.Timestamp{}}) 661 if err != nil { 662 return nil, err 663 } 664 665 sub, err := ctx.GetSubscriptionMeta(dbName, Snapshot{TS: ×tamp.Timestamp{}}) 666 if err != nil { 667 return nil, err 668 } 669 670 ddlType := plan.DataDefinition_SHOW_TABLES 671 subName := dbName 672 var sql string 673 if sub != nil { 674 accountId = uint32(sub.AccountId) 675 dbName = sub.DbName 676 ctx.SetQueryingSubscription(sub) 677 defer func() { 678 ctx.SetQueryingSubscription(nil) 679 }() 680 681 if accountId == catalog.System_Account { 682 mustShowTable := "relname = 'mo_database' or relname = 'mo_tables' or relname = 'mo_columns'" 683 clusterTable := fmt.Sprintf(" or relkind = '%s'", catalog.SystemClusterRel) 684 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 685 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) and relkind != '%s'", 686 subName, MO_CATALOG_DB_NAME, dbName, catalog.MOAutoIncrTable, catalog.IndexTableNamePrefix+"%", accountClause, catalog.SystemViewRel) 687 } else { 688 sql = "SELECT count(relname) `Number of tables in %s` FROM %s.mo_tables WHERE reldatabase = '%s' and relname != '%s' and relname not like '%s'and relkind != '%s'" 689 sql = fmt.Sprintf(sql, subName, MO_CATALOG_DB_NAME, dbName, catalog.MOAutoIncrTable, catalog.IndexTableNamePrefix+"%", catalog.SystemViewRel) 690 } 691 } else { 692 if accountId == catalog.System_Account { 693 mustShowTable := "relname = 'mo_database' or relname = 'mo_tables' or relname = 'mo_columns'" 694 clusterTable := fmt.Sprintf(" or relkind = '%s'", catalog.SystemClusterRel) 695 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 696 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)", 697 subName, MO_CATALOG_DB_NAME, dbName, catalog.MOAutoIncrTable, catalog.IndexTableNamePrefix+"%", accountClause) 698 } else { 699 sql = "SELECT count(relname) `Number of tables in %s` FROM %s.mo_tables WHERE reldatabase = '%s' and relname != '%s' and relname not like '%s'" 700 sql = fmt.Sprintf(sql, subName, MO_CATALOG_DB_NAME, dbName, catalog.MOAutoIncrTable, catalog.IndexTableNamePrefix+"%") 701 } 702 703 } 704 705 return returnByRewriteSQL(ctx, sql, ddlType) 706 } 707 708 func buildShowColumnNumber(stmt *tree.ShowColumnNumber, ctx CompilerContext) (*Plan, error) { 709 accountId, err := ctx.GetAccountId() 710 if err != nil { 711 return nil, err 712 } 713 // snapshot to fix 714 dbName, err := databaseIsValid(getSuitableDBName(stmt.Table.GetDBName(), stmt.DbName), ctx, Snapshot{TS: ×tamp.Timestamp{}}) 715 if err != nil { 716 return nil, err 717 } 718 719 tblName := string(stmt.Table.ToTableName().ObjectName) 720 obj, tableDef := ctx.Resolve(dbName, tblName, Snapshot{TS: ×tamp.Timestamp{}}) 721 if tableDef == nil { 722 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 723 } 724 725 ddlType := plan.DataDefinition_SHOW_COLUMNS 726 var sql string 727 728 var sub *SubscriptionMeta 729 if obj.PubInfo != nil { 730 accountId = uint32(obj.PubInfo.GetTenantId()) 731 dbName = obj.SchemaName 732 sub = &SubscriptionMeta{ 733 AccountId: obj.PubInfo.GetTenantId(), 734 } 735 ctx.SetQueryingSubscription(sub) 736 defer func() { 737 ctx.SetQueryingSubscription(nil) 738 }() 739 } 740 741 if accountId == catalog.System_Account { 742 mustShowTable := "att_relname = 'mo_database' or att_relname = 'mo_tables' or att_relname = 'mo_columns'" 743 clusterTable := "" 744 if util.TableIsClusterTable(tableDef.GetTableType()) { 745 clusterTable = fmt.Sprintf(" or att_relname = '%s'", tblName) 746 } 747 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 748 sql = "SELECT count(attname) `Number of columns in %s` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND (%s) AND att_is_hidden = 0" 749 sql = fmt.Sprintf(sql, tblName, MO_CATALOG_DB_NAME, dbName, tblName, accountClause) 750 } else { 751 sql = "SELECT count(attname) `Number of columns in %s` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s'AND att_is_hidden = 0" 752 sql = fmt.Sprintf(sql, tblName, MO_CATALOG_DB_NAME, dbName, tblName) 753 } 754 755 return returnByRewriteSQL(ctx, sql, ddlType) 756 } 757 758 func buildShowTableValues(stmt *tree.ShowTableValues, ctx CompilerContext) (*Plan, error) { 759 dbName, err := databaseIsValid(getSuitableDBName(stmt.Table.GetDBName(), stmt.DbName), ctx, Snapshot{TS: ×tamp.Timestamp{}}) 760 if err != nil { 761 return nil, err 762 } 763 764 tblName := string(stmt.Table.ToTableName().ObjectName) 765 obj, tableDef := ctx.Resolve(dbName, tblName, Snapshot{TS: ×tamp.Timestamp{}}) 766 if tableDef == nil { 767 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 768 } 769 770 if obj.PubInfo != nil { 771 sub := &SubscriptionMeta{ 772 AccountId: obj.PubInfo.GetTenantId(), 773 } 774 ctx.SetQueryingSubscription(sub) 775 defer func() { 776 ctx.SetQueryingSubscription(nil) 777 }() 778 } 779 780 ddlType := plan.DataDefinition_SHOW_TARGET 781 782 sql := "SELECT" 783 isAllNull := true 784 for _, col := range tableDef.Cols { 785 if col.Hidden { 786 continue 787 } 788 colName := col.Name 789 if types.T(col.GetTyp().Id) == types.T_json { 790 sql += " null as `max(%s)`, null as `min(%s)`," 791 sql = fmt.Sprintf(sql, colName, colName) 792 } else { 793 sql += " max(%s), min(%s)," 794 sql = fmt.Sprintf(sql, colName, colName) 795 isAllNull = false 796 } 797 } 798 sql = sql[:len(sql)-1] 799 sql += " FROM %s" 800 801 if isAllNull { 802 sql += " LIMIT 1" 803 } 804 sql = fmt.Sprintf(sql, tblName) 805 806 return returnByRewriteSQL(ctx, sql, ddlType) 807 } 808 809 func buildShowColumns(stmt *tree.ShowColumns, ctx CompilerContext) (*Plan, error) { 810 if stmt.Like != nil && stmt.Where != nil { 811 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 812 } 813 814 accountId, err := ctx.GetAccountId() 815 if err != nil { 816 return nil, err 817 } 818 dbName, err := databaseIsValid(getSuitableDBName(stmt.Table.GetDBName(), stmt.DBName), ctx, Snapshot{TS: ×tamp.Timestamp{}}) 819 if err != nil { 820 return nil, err 821 } 822 823 tblName := string(stmt.Table.ToTableName().ObjectName) 824 obj, tableDef := ctx.Resolve(dbName, tblName, Snapshot{TS: ×tamp.Timestamp{}}) 825 if tableDef == nil { 826 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 827 } 828 var sub *SubscriptionMeta 829 if obj.PubInfo != nil { 830 dbName = obj.SchemaName 831 accountId = uint32(obj.PubInfo.GetTenantId()) 832 sub = &SubscriptionMeta{ 833 AccountId: obj.PubInfo.GetTenantId(), 834 } 835 ctx.SetQueryingSubscription(sub) 836 defer func() { 837 ctx.SetQueryingSubscription(nil) 838 }() 839 } 840 var keyStr string 841 if dbName == catalog.MO_CATALOG && tblName == catalog.MO_DATABASE { 842 keyStr = "case when attname = '" + catalog.SystemDBAttr_ID + "' then 'PRI' else '' END as `Key`" 843 } else if dbName == catalog.MO_CATALOG && tblName == catalog.MO_TABLES { 844 keyStr = "case when attname = '" + catalog.SystemRelAttr_ID + "' then 'PRI' else '' END as `Key`" 845 } else if dbName == catalog.MO_CATALOG && tblName == catalog.MO_COLUMNS { 846 keyStr = "case when attname = '" + catalog.SystemColAttr_UniqName + "' then 'PRI' else '' END as `Key`" 847 } else { 848 if tableDef.Pkey != nil || len(tableDef.Fkeys) != 0 || len(tableDef.Indexes) != 0 { 849 keyStr += "case" 850 if tableDef.Pkey != nil { 851 for _, name := range tableDef.Pkey.Names { 852 keyStr += " when attname = " 853 keyStr += "'" + name + "'" 854 keyStr += " then 'PRI'" 855 } 856 } 857 if len(tableDef.Fkeys) != 0 { 858 colIdToName := make(map[uint64]string) 859 for _, col := range tableDef.Cols { 860 if col.Hidden { 861 continue 862 } 863 colIdToName[col.ColId] = col.Name 864 } 865 for _, fk := range tableDef.Fkeys { 866 for _, colId := range fk.Cols { 867 keyStr += " when attname = " 868 keyStr += "'" + colIdToName[colId] + "'" 869 keyStr += " then 'MUL'" 870 } 871 } 872 } 873 if tableDef.Indexes != nil { 874 for _, indexdef := range tableDef.Indexes { 875 name := indexdef.Parts[0] 876 if indexdef.Unique { 877 if isPrimaryKey(tableDef, indexdef.Parts) { 878 for _, name := range indexdef.Parts { 879 keyStr += " when attname = " 880 keyStr += "'" + name + "'" 881 keyStr += " then 'PRI'" 882 } 883 } else if isMultiplePriKey(indexdef) { 884 keyStr += " when attname = " 885 keyStr += "'" + name + "'" 886 keyStr += " then 'MUL'" 887 } else { 888 keyStr += " when attname = " 889 keyStr += "'" + name + "'" 890 keyStr += " then 'UNI'" 891 } 892 } else { 893 keyStr += " when attname = " 894 keyStr += "'" + name + "'" 895 keyStr += " then 'MUL'" 896 } 897 } 898 } 899 keyStr += " else '' END as `Key`" 900 } else { 901 keyStr = "'' as `Key`" 902 } 903 } 904 905 ddlType := plan.DataDefinition_SHOW_COLUMNS 906 907 var sql string 908 if accountId == catalog.System_Account { 909 mustShowTable := "att_relname = 'mo_database' or att_relname = 'mo_tables' or att_relname = 'mo_columns'" 910 clusterTable := "" 911 if util.TableIsClusterTable(tableDef.GetTableType()) { 912 clusterTable = fmt.Sprintf(" or att_relname = '%s'", tblName) 913 } 914 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable+clusterTable) 915 sql = "SELECT attname `Field`, CASE WHEN LENGTH(attr_enum) > 0 THEN mo_show_visible_bin_enum(atttyp, attr_enum) ELSE mo_show_visible_bin(atttyp, 3) END AS `Type`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, mo_show_visible_bin(att_default, 1) `Default`, '' `Extra`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND (%s) AND att_is_hidden = 0 ORDER BY attnum" 916 if stmt.Full { 917 sql = "SELECT attname `Field`, CASE WHEN LENGTH(attr_enum) > 0 THEN mo_show_visible_bin_enum(atttyp, attr_enum) ELSE mo_show_visible_bin(atttyp, 3) END AS `Type`, null `Collation`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, mo_show_visible_bin(att_default, 1) `Default`, '' `Extra`,'select,insert,update,references' `Privileges`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND (%s) AND att_is_hidden = 0 ORDER BY attnum" 918 } 919 sql = fmt.Sprintf(sql, keyStr, MO_CATALOG_DB_NAME, dbName, tblName, accountClause) 920 } else { 921 sql = "SELECT attname `Field`, CASE WHEN LENGTH(attr_enum) > 0 THEN mo_show_visible_bin_enum(atttyp, attr_enum) ELSE mo_show_visible_bin(atttyp, 3) END AS `Type`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, mo_show_visible_bin(att_default, 1) `Default`, '' `Extra`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND att_is_hidden = 0 ORDER BY attnum" 922 if stmt.Full { 923 sql = "SELECT attname `Field`, CASE WHEN LENGTH(attr_enum) > 0 THEN mo_show_visible_bin_enum(atttyp, attr_enum) ELSE mo_show_visible_bin(atttyp, 3) END AS `Type`, null `Collation`, iff(attnotnull = 0, 'YES', 'NO') `Null`, %s, mo_show_visible_bin(att_default, 1) `Default`, '' `Extra`,'select,insert,update,references' `Privileges`, att_comment `Comment` FROM %s.mo_columns WHERE att_database = '%s' AND att_relname = '%s' AND att_is_hidden = 0 ORDER BY attnum" 924 } 925 sql = fmt.Sprintf(sql, keyStr, MO_CATALOG_DB_NAME, dbName, tblName) 926 } 927 928 if stmt.Where != nil { 929 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 930 } 931 932 if stmt.Like != nil { 933 // append filter [AND ma.attname like stmt.Like] to WHERE clause 934 likeExpr := stmt.Like 935 likeExpr.Left = tree.SetUnresolvedName("attname") 936 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 937 } 938 939 return returnByRewriteSQL(ctx, sql, ddlType) 940 } 941 942 func buildShowTableStatus(stmt *tree.ShowTableStatus, ctx CompilerContext) (*Plan, error) { 943 if stmt.Like != nil && stmt.Where != nil { 944 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 945 } 946 947 dbName, err := databaseIsValid(stmt.DbName, ctx, Snapshot{TS: ×tamp.Timestamp{}}) 948 if err != nil { 949 return nil, err 950 } 951 952 stmt.DbName = dbName 953 954 ddlType := plan.DataDefinition_SHOW_TABLE_STATUS 955 accountId, err := ctx.GetAccountId() 956 if err != nil { 957 return nil, err 958 } 959 960 sub, err := ctx.GetSubscriptionMeta(dbName, Snapshot{TS: ×tamp.Timestamp{}}) 961 if err != nil { 962 return nil, err 963 } 964 if sub != nil { 965 accountId = uint32(sub.AccountId) 966 dbName = sub.DbName 967 ctx.SetQueryingSubscription(sub) 968 defer func() { 969 ctx.SetQueryingSubscription(nil) 970 }() 971 } 972 973 mustShowTable := "relname = 'mo_database' or relname = 'mo_tables' or relname = 'mo_columns'" 974 accountClause := fmt.Sprintf("account_id = %v or (account_id = 0 and (%s))", accountId, mustShowTable) 975 sql := `select 976 relname as 'Name', 977 'Tae' as 'Engine', 978 'Dynamic' as 'Row_format', 979 0 as 'Rows', 980 0 as 'Avg_row_length', 981 0 as 'Data_length', 982 0 as 'Max_data_length', 983 0 as 'Index_length', 984 'NULL' as 'Data_free', 985 0 as 'Auto_increment', 986 created_time as 'Create_time', 987 'NULL' as 'Update_time', 988 'NULL' as 'Check_time', 989 'utf-8' as 'Collation', 990 'NULL' as 'Checksum', 991 '' as 'Create_options', 992 rel_comment as 'Comment', 993 owner as 'Role_id', 994 '-' as 'Role_name' 995 from 996 %s.mo_tables 997 where 998 reldatabase = '%s' 999 and relkind != '%s' 1000 and relname != '%s' 1001 and relname not like '%s' 1002 and (%s)` 1003 sql = fmt.Sprintf(sql, MO_CATALOG_DB_NAME, dbName, catalog.SystemPartitionRel, catalog.MOAutoIncrTable, catalog.IndexTableNamePrefix+"%", accountClause) 1004 1005 if stmt.Where != nil { 1006 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 1007 } 1008 1009 if stmt.Like != nil { 1010 // append filter [AND ma.relname like stmt.Like] to WHERE clause 1011 likeExpr := stmt.Like 1012 likeExpr.Left = tree.SetUnresolvedName("relname") 1013 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 1014 } 1015 1016 return returnByRewriteSQL(ctx, sql, ddlType) 1017 } 1018 1019 // TODO: Implement show target 1020 func buildShowTarget(stmt *tree.ShowTarget, ctx CompilerContext) (*Plan, error) { 1021 ddlType := plan.DataDefinition_SHOW_TARGET 1022 sql := "" 1023 switch stmt.Type { 1024 case tree.ShowCharset: 1025 sql = "select '' as `Charset`, '' as `Description`, '' as `Default collation`, '' as `Maxlen` where 0" 1026 case tree.ShowTriggers: 1027 return buildShowTriggers(stmt, ctx) 1028 default: 1029 sql = "select 1 where 0" 1030 } 1031 return returnByRewriteSQL(ctx, sql, ddlType) 1032 } 1033 1034 func buildShowLocks(stmt *tree.ShowLocks, ctx CompilerContext) (*Plan, error) { 1035 ddlType := plan.DataDefinition_SHOW_TARGET 1036 sql := "select 1 where 0" 1037 return returnByRewriteSQL(ctx, sql, ddlType) 1038 } 1039 1040 func buildShowNodeList(stmt *tree.ShowNodeList, ctx CompilerContext) (*Plan, error) { 1041 ddlType := plan.DataDefinition_SHOW_TARGET 1042 sql := "select 1 where 0" 1043 return returnByRewriteSQL(ctx, sql, ddlType) 1044 } 1045 1046 func buildShowFunctionOrProcedureStatus(stmt *tree.ShowFunctionOrProcedureStatus, ctx CompilerContext) (*Plan, error) { 1047 var sql string 1048 1049 ddlType := plan.DataDefinition_SHOW_TARGET 1050 if stmt.Like != nil && stmt.Where != nil { 1051 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 1052 } 1053 1054 if stmt.IsFunction { 1055 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) 1056 } else { 1057 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_stored_procedure", MO_CATALOG_DB_NAME) 1058 } 1059 1060 if stmt.Where != nil { 1061 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 1062 } 1063 1064 if stmt.Like != nil { 1065 // append filter [AND ma.attname like stmt.Like] to WHERE clause 1066 likeExpr := stmt.Like 1067 likeExpr.Left = tree.SetUnresolvedName("name") 1068 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 1069 } 1070 1071 return returnByRewriteSQL(ctx, sql, ddlType) 1072 } 1073 1074 func buildShowTriggers(stmt *tree.ShowTarget, ctx CompilerContext) (*Plan, error) { 1075 if stmt.Like != nil && stmt.Where != nil { 1076 return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 1077 } 1078 1079 dbName, err := databaseIsValid(stmt.DbName, ctx, Snapshot{TS: ×tamp.Timestamp{}}) 1080 if err != nil { 1081 return nil, err 1082 } 1083 stmt.DbName = dbName 1084 1085 ddlType := plan.DataDefinition_SHOW_TARGET 1086 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) 1087 1088 if stmt.Where != nil { 1089 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 1090 } 1091 1092 if stmt.Like != nil { 1093 // append filter [AND ma.attname like stmt.Like] to WHERE clause 1094 likeExpr := stmt.Like 1095 likeExpr.Left = tree.SetUnresolvedName("event_object_table") 1096 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 1097 } 1098 1099 return returnByRewriteSQL(ctx, sql, ddlType) 1100 } 1101 1102 func buildShowIndex(stmt *tree.ShowIndex, ctx CompilerContext) (*Plan, error) { 1103 dbName, err := databaseIsValid(getSuitableDBName(stmt.TableName.GetDBName(), stmt.DbName), ctx, Snapshot{TS: ×tamp.Timestamp{}}) 1104 if err != nil { 1105 return nil, err 1106 } 1107 tblName := stmt.TableName.GetTableName() 1108 obj, tableDef := ctx.Resolve(dbName, tblName, Snapshot{TS: ×tamp.Timestamp{}}) 1109 if tableDef == nil { 1110 return nil, moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 1111 } 1112 1113 ddlType := plan.DataDefinition_SHOW_INDEX 1114 1115 if obj.PubInfo != nil { 1116 sub := &SubscriptionMeta{ 1117 AccountId: obj.PubInfo.GetTenantId(), 1118 } 1119 dbName = obj.SchemaName 1120 ctx.SetQueryingSubscription(sub) 1121 defer func() { 1122 ctx.SetQueryingSubscription(nil) 1123 }() 1124 } 1125 1126 sql := "select " + 1127 "`tcl`.`att_relname` as `Table`, " + 1128 "if(`idx`.`type` = 'MULTIPLE', 1, 0) as `Non_unique`, " + 1129 "`idx`.`name` as `Key_name`, " + 1130 "`idx`.`ordinal_position` as `Seq_in_index`, " + 1131 "`idx`.`column_name` as `Column_name`, " + 1132 "'A' as `Collation`, 0 as `Cardinality`, " + 1133 "'NULL' as `Sub_part`, " + 1134 "'NULL' as `Packed`, " + 1135 "if(`tcl`.`attnotnull` = 0, 'YES', '') as `Null`, " + 1136 "`idx`.`algo` as 'Index_type', " + 1137 "'' as `Comment`, " + 1138 "`idx`.`comment` as `Index_comment`, " + 1139 "`idx`.`algo_params` as `Index_params`, " + 1140 "if(`idx`.`is_visible` = 1, 'YES', 'NO') as `Visible`, " + 1141 "'NULL' as `Expression` " + 1142 "from `%s`.`mo_indexes` `idx` left join `%s`.`mo_columns` `tcl` " + 1143 "on (`idx`.`table_id` = `tcl`.`att_relname_id` and `idx`.`column_name` = `tcl`.`attname`) " + 1144 "where `tcl`.`att_database` = '%s' AND " + 1145 "`tcl`.`att_relname` = '%s' AND " + 1146 "`idx`.`column_name` NOT LIKE '%s' " + 1147 // Below `GROUP BY` is used instead of DISTINCT(`idx`.`name`) to handle IVF-FLAT or multi table indexes scenarios. 1148 // NOTE: We need to add all the table column names to the GROUP BY clause 1149 // 1150 // Without `GROUP BY`, we will printing the same index multiple times for IVFFLAT index. 1151 // (there are multiple entries in mo_indexes for the same index, with differing algo_table_type and index_table_name). 1152 // mysql> show index from tbl; 1153 //+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+-----------------------------------------+---------+------------+ 1154 //| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Index_params | Visible | Expression | 1155 //+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+-----------------------------------------+---------+------------+ 1156 //| tbl | 1 | idx1 | 1 | embedding | A | 0 | NULL | NULL | YES | ivfflat | | | {"lists":"2","op_type":"vector_l2_ops"} | YES | NULL | 1157 //| tbl | 1 | idx1 | 1 | embedding | A | 0 | NULL | NULL | YES | ivfflat | | | {"lists":"2","op_type":"vector_l2_ops"} | YES | NULL | 1158 //| tbl | 1 | idx1 | 1 | embedding | A | 0 | NULL | NULL | YES | ivfflat | | | {"lists":"2","op_type":"vector_l2_ops"} | YES | NULL | 1159 //| tbl | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | | | | | YES | NULL | 1160 //+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+-----------------------------------------+---------+------------+ 1161 // 1162 // With `GROUP BY`, we print 1163 // mysql> show index from tbl; 1164 //+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+-----------------------------------------+---------+------------+ 1165 //| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Index_params | Visible | Expression | 1166 //+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+-----------------------------------------+---------+------------+ 1167 //| tbl | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | | | | | YES | NULL | 1168 //| tbl | 1 | idx1 | 1 | embedding | A | 0 | NULL | NULL | YES | ivfflat | | | {"lists":"2","op_type":"vector_l2_ops"} | YES | NULL | 1169 //+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+-----------------------------------------+---------+------------+ 1170 "GROUP BY `tcl`.`att_relname`, `idx`.`type`, `idx`.`name`, `idx`.`ordinal_position`, " + 1171 "`idx`.`column_name`, `tcl`.`attnotnull`, `idx`.`algo`, `idx`.`comment`, " + 1172 "`idx`.`algo_params`, `idx`.`is_visible`" + 1173 ";" 1174 showIndexSql := fmt.Sprintf(sql, MO_CATALOG_DB_NAME, MO_CATALOG_DB_NAME, dbName, tblName, catalog.AliasPrefix+"%") 1175 1176 if stmt.Where != nil { 1177 return returnByWhereAndBaseSQL(ctx, showIndexSql, stmt.Where, ddlType) 1178 } 1179 return returnByRewriteSQL(ctx, showIndexSql, ddlType) 1180 } 1181 1182 // TODO: Improve SQL. Currently, Lack of the mata of grants 1183 func buildShowGrants(stmt *tree.ShowGrants, ctx CompilerContext) (*Plan, error) { 1184 1185 ddlType := plan.DataDefinition_SHOW_TARGET 1186 if stmt.ShowGrantType == tree.GrantForRole { 1187 role_name := stmt.Roles[0].UserName 1188 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';" 1189 sql = fmt.Sprintf(sql, role_name, role_name, MO_CATALOG_DB_NAME, role_name) 1190 return returnByRewriteSQL(ctx, sql, ddlType) 1191 } else { 1192 if stmt.Hostname == "" { 1193 stmt.Hostname = MO_DEFUALT_HOSTNAME 1194 } 1195 if stmt.Username == "" { 1196 stmt.Username = ctx.GetUserName() 1197 } 1198 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';" 1199 sql = fmt.Sprintf(sql, stmt.Username, stmt.Hostname, stmt.Username, stmt.Username, stmt.Hostname) 1200 return returnByRewriteSQL(ctx, sql, ddlType) 1201 } 1202 } 1203 1204 func buildShowRoles(stmt *tree.ShowRolesStmt, ctx CompilerContext) (*Plan, error) { 1205 ddlType := plan.DataDefinition_SHOW_TARGET 1206 sql := fmt.Sprintf("SELECT role_name as `ROLE_NAME`, creator as `CREATOR`, created_time as `CREATED_TIME`, comments as `COMMENTS` FROM %s.mo_role;", MO_CATALOG_DB_NAME) 1207 1208 if stmt.Like != nil { 1209 // append filter [AND mo_role.role_name like stmt.Like] to WHERE clause 1210 likeExpr := stmt.Like 1211 likeExpr.Left = tree.SetUnresolvedName("role_name") 1212 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 1213 } 1214 1215 return returnByRewriteSQL(ctx, sql, ddlType) 1216 } 1217 1218 func buildShowStages(stmt *tree.ShowStages, ctx CompilerContext) (*Plan, error) { 1219 ddlType := plan.DataDefinition_SHOW_TARGET 1220 sql := fmt.Sprintf("SELECT stage_name as `STAGE_NAME`, url as `URL`, case stage_status when 'enabled' then 'ENABLED' else 'DISABLED' end as `STATUS`, comment as `COMMENT` FROM %s.mo_stages;", MO_CATALOG_DB_NAME) 1221 1222 if stmt.Like != nil { 1223 // append filter [AND mo_stages.stage_name like stmt.Like] to WHERE clause 1224 likeExpr := stmt.Like 1225 likeExpr.Left = tree.SetUnresolvedName("stage_name") 1226 return returnByLikeAndSQL(ctx, sql, likeExpr, ddlType) 1227 } 1228 1229 return returnByRewriteSQL(ctx, sql, ddlType) 1230 } 1231 1232 func buildShowSnapShots(stmt *tree.ShowSnapShots, ctx CompilerContext) (*Plan, error) { 1233 ddlType := plan.DataDefinition_SHOW_TARGET 1234 sql := fmt.Sprintf("SELECT sname as `SNAPSHOT_NAME`, CAST_NANO_TO_TIMESTAMP(ts) as `TIMESTAMP`, level as `SNAPSHOT_LEVEL`, account_name as `ACCOUNT_NAME`, database_name as `DATABASE_NAME`, table_name as `TABLE_NAME` FROM %s.mo_snapshots ORDER BY ts DESC", MO_CATALOG_DB_NAME) 1235 1236 if stmt.Where != nil { 1237 return returnByWhereAndBaseSQL(ctx, sql, stmt.Where, ddlType) 1238 } 1239 1240 return returnByRewriteSQL(ctx, sql, ddlType) 1241 } 1242 1243 func buildShowAccountUpgrade(stmt *tree.ShowAccountUpgrade, ctx CompilerContext) (*Plan, error) { 1244 ddlType := plan.DataDefinition_SHOW_UPGRADE 1245 sql := fmt.Sprintf("select account_name as `account_name`, create_version as `current_version` from %s.mo_account order by account_id;", MO_CATALOG_DB_NAME) 1246 return returnByRewriteSQL(ctx, sql, ddlType) 1247 } 1248 1249 func buildShowVariables(stmt *tree.ShowVariables, ctx CompilerContext) (*Plan, error) { 1250 showVariables := &plan.ShowVariables{ 1251 Global: stmt.Global, 1252 } 1253 1254 // we deal with 'show vriables' statement in frontend now. 1255 // so just return an empty plan in building plan for prepare statment is ok. 1256 1257 // if stmt.Like != nil && stmt.Where != nil { 1258 // return nil, moerr.NewSyntaxError(ctx.GetContext(), "like clause and where clause cannot exist at the same time") 1259 // } 1260 1261 // builder := NewQueryBuilder(plan.Query_SELECT, ctx) 1262 // binder := NewWhereBinder(builder, &BindContext{}) 1263 1264 // if stmt.Like != nil { 1265 // // here will error because stmt.Like.Left is nil, you need add left expr like : stmt.Like.Left = tree.SetUnresolvedName("column_name") 1266 // // but we have no column name, because Variables is save in a hashmap in frontend, not a table. 1267 // expr, err := binder.bindComparisonExpr(stmt.Like, 0, false) 1268 // if err != nil { 1269 // return nil, err 1270 // } 1271 // showVariables.Where = append(showVariables.Where, expr) 1272 // } 1273 // if stmt.Where != nil { 1274 // exprs, err := splitAndBindCondition(stmt.Where.Expr, &BindContext{}) 1275 // if err != nil { 1276 // return nil, err 1277 // } 1278 // showVariables.Where = append(showVariables.Where, exprs...) 1279 // } 1280 1281 return &Plan{ 1282 Plan: &plan.Plan_Ddl{ 1283 Ddl: &plan.DataDefinition{ 1284 DdlType: plan.DataDefinition_SHOW_VARIABLES, 1285 Definition: &plan.DataDefinition_ShowVariables{ 1286 ShowVariables: showVariables, 1287 }, 1288 }, 1289 }, 1290 }, nil 1291 } 1292 1293 func buildShowStatus(stmt *tree.ShowStatus, ctx CompilerContext) (*Plan, error) { 1294 ddlType := plan.DataDefinition_SHOW_STATUS 1295 sql := "select '' as `Variable_name`, '' as `Value` where 0" 1296 return returnByRewriteSQL(ctx, sql, ddlType) 1297 } 1298 1299 func buildShowProcessList(ctx CompilerContext) (*Plan, error) { 1300 ddlType := plan.DataDefinition_SHOW_PROCESSLIST 1301 // "show processlist" is implemented by table function processlist(). 1302 sql := "select * from processlist() a" 1303 return returnByRewriteSQL(ctx, sql, ddlType) 1304 } 1305 1306 func buildShowPublication(stmt *tree.ShowPublications, ctx CompilerContext) (*Plan, error) { 1307 ddlType := plan.DataDefinition_SHOW_TARGET 1308 sql := "select" + 1309 " pub_name as `publication`," + 1310 " database_name as `database`," + 1311 " created_time as `create_time`," + 1312 " update_time as `update_time`," + 1313 " case account_list " + 1314 " when 'all' then cast('*' as text)" + 1315 " else account_list" + 1316 " end as `sub_account`," + 1317 " comment as `comments`" + 1318 " from mo_catalog.mo_pubs" 1319 like := stmt.Like 1320 if like != nil { 1321 right, ok := like.Right.(*tree.NumVal) 1322 if !ok || right.Value.Kind() != constant.String { 1323 return nil, moerr.NewInternalError(ctx.GetContext(), "like clause must be a string") 1324 } 1325 sql += fmt.Sprintf(" where pub_name like '%s' order by pub_name;", constant.StringVal(right.Value)) 1326 } else { 1327 sql += " order by update_time desc, created_time desc;" 1328 } 1329 return returnByRewriteSQL(ctx, sql, ddlType) 1330 } 1331 1332 func buildShowCreatePublications(stmt *tree.ShowCreatePublications, ctx CompilerContext) (*Plan, error) { 1333 ddlType := plan.DataDefinition_SHOW_TARGET 1334 sql := fmt.Sprintf("select pub_name as Publication, 'CREATE PUBLICATION ' || pub_name || ' DATABASE ' || database_name || ' ACCOUNT ' || account_list as 'Create Publication' from mo_catalog.mo_pubs where pub_name='%s';", stmt.Name) 1335 return returnByRewriteSQL(ctx, sql, ddlType) 1336 } 1337 1338 func returnByRewriteSQL(ctx CompilerContext, sql string, 1339 ddlType plan.DataDefinition_DdlType) (*Plan, error) { 1340 newStmt, err := getRewriteSQLStmt(ctx, sql) 1341 defer newStmt.Free() 1342 if err != nil { 1343 return nil, err 1344 } 1345 return getReturnDdlBySelectStmt(ctx, newStmt, ddlType) 1346 } 1347 1348 func returnByWhereAndBaseSQL(ctx CompilerContext, baseSQL string, 1349 where *tree.Where, ddlType plan.DataDefinition_DdlType) (*Plan, error) { 1350 sql := fmt.Sprintf("SELECT * FROM (%s) tbl", baseSQL) 1351 // logutil.Info(sql) 1352 newStmt, err := getRewriteSQLStmt(ctx, sql) 1353 defer newStmt.Free() 1354 if err != nil { 1355 return nil, err 1356 } 1357 // set show statement's where clause to new statement 1358 newStmt.(*tree.Select).Select.(*tree.SelectClause).Where = where 1359 return getReturnDdlBySelectStmt(ctx, newStmt, ddlType) 1360 } 1361 1362 func returnByLikeAndSQL(ctx CompilerContext, sql string, like *tree.ComparisonExpr, 1363 ddlType plan.DataDefinition_DdlType) (*Plan, error) { 1364 newStmt, err := getRewriteSQLStmt(ctx, sql) 1365 defer newStmt.Free() 1366 if err != nil { 1367 return nil, err 1368 } 1369 var whereExpr *tree.Where 1370 1371 if newStmt.(*tree.Select).Select.(*tree.SelectClause).Where == nil { 1372 whereExpr = &tree.Where{ 1373 Type: "where", 1374 Expr: like, 1375 } 1376 } else { 1377 whereExpr = &tree.Where{ 1378 Type: "where", 1379 Expr: &tree.AndExpr{ 1380 Left: newStmt.(*tree.Select).Select.(*tree.SelectClause).Where.Expr, 1381 Right: like, 1382 }, 1383 } 1384 } 1385 // set show statement's like clause to new statement 1386 newStmt.(*tree.Select).Select.(*tree.SelectClause).Where = whereExpr 1387 // logutil.Info(tree.String(newStmt, dialect.MYSQL)) 1388 return getReturnDdlBySelectStmt(ctx, newStmt, ddlType) 1389 } 1390 1391 func getRewriteSQLStmt(ctx CompilerContext, sql string) (tree.Statement, error) { 1392 newStmts, err := parsers.Parse(ctx.GetContext(), dialect.MYSQL, sql, 1, 0) 1393 if err != nil { 1394 return nil, err 1395 } 1396 if len(newStmts) != 1 { 1397 return nil, moerr.NewInvalidInput(ctx.GetContext(), "rewrite can only contain one statement, %d provided", len(newStmts)) 1398 } 1399 return newStmts[0], nil 1400 } 1401 1402 func getReturnDdlBySelectStmt(ctx CompilerContext, stmt tree.Statement, 1403 ddlType plan.DataDefinition_DdlType) (*Plan, error) { 1404 queryPlan, err := BuildPlan(ctx, stmt, false) 1405 if err != nil { 1406 return nil, err 1407 } 1408 return queryPlan, nil 1409 // return &Plan{ 1410 // Plan: &plan.Plan_Ddl{ 1411 // Ddl: &plan.DataDefinition{ 1412 // DdlType: ddlType, 1413 // Query: queryPlan.GetQuery(), 1414 // }, 1415 // }, 1416 // }, nil 1417 }