github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/snapshot.go (about) 1 // Copyright 2021 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 frontend 16 17 import ( 18 "context" 19 "fmt" 20 "slices" 21 "time" 22 23 "github.com/google/uuid" 24 25 "github.com/matrixorigin/matrixone/pkg/common/moerr" 26 "github.com/matrixorigin/matrixone/pkg/defines" 27 pbplan "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/plan" 33 ) 34 35 type tableType string 36 37 const view tableType = "VIEW" 38 39 var ( 40 insertIntoMoSnapshots = `insert into mo_catalog.mo_snapshots( 41 snapshot_id, 42 sname, 43 ts, 44 level, 45 account_name, 46 database_name, 47 table_name, 48 obj_id ) values ('%s', '%s', %d, '%s', '%s', '%s', '%s', %d);` 49 50 dropSnapshotFormat = `delete from mo_catalog.mo_snapshots where sname = '%s' order by snapshot_id;` 51 52 checkSnapshotFormat = `select snapshot_id from mo_catalog.mo_snapshots where sname = "%s" order by snapshot_id;` 53 54 getSnapshotTsWithSnapshotNameFormat = `select ts from mo_catalog.mo_snapshots where sname = "%s" order by snapshot_id;` 55 56 getSnapshotFormat = `select * from mo_catalog.mo_snapshots` 57 58 checkSnapshotTsFormat = `select snapshot_id from mo_catalog.mo_snapshots where ts = %d order by snapshot_id;` 59 60 restoreTableDataFmt = "insert into `%s`.`%s` SELECT * FROM `%s`.`%s` {snapshot = '%s'}" 61 62 skipDbs = []string{"mysql", "system", "system_metrics", "mo_task", "mo_debug", "information_schema", "mo_catalog"} 63 ) 64 65 type snapshotRecord struct { 66 snapshotId string 67 snapshotName string 68 ts int64 69 level string 70 accountName string 71 databaseName string 72 tableName string 73 objId uint64 74 } 75 76 type tableInfo struct { 77 dbName string 78 tblName string 79 typ tableType 80 createSql string 81 } 82 83 func doCreateSnapshot(ctx context.Context, ses *Session, stmt *tree.CreateSnapShot) error { 84 var err error 85 var snapshotLevel tree.SnapshotLevel 86 var snapshotForAccount string 87 var snapshotName string 88 var snapshotExist bool 89 var snapshotId string 90 var databaseName string 91 var tableName string 92 var sql string 93 var objId uint64 94 95 // check create stage priv 96 err = doCheckRole(ctx, ses) 97 if err != nil { 98 return err 99 } 100 101 bh := ses.GetBackgroundExec(ctx) 102 defer bh.Close() 103 err = bh.Exec(ctx, "begin;") 104 defer func() { 105 err = finishTxn(ctx, bh, err) 106 }() 107 if err != nil { 108 return err 109 } 110 111 // check create snapshot priv 112 113 // 1.only admin can create tenant level snapshot 114 err = doCheckRole(ctx, ses) 115 if err != nil { 116 return err 117 } 118 // 2.only sys can create cluster level snapshot 119 tenantInfo := ses.GetTenantInfo() 120 currentAccount := tenantInfo.GetTenant() 121 snapshotLevel = stmt.Object.SLevel.Level 122 if snapshotLevel == tree.SNAPSHOTLEVELCLUSTER && currentAccount != sysAccountName { 123 return moerr.NewInternalError(ctx, "only sys tenant can create cluster level snapshot") 124 } 125 126 // 3.only sys can create tenant level snapshot for other tenant 127 if snapshotLevel == tree.SNAPSHOTLEVELACCOUNT { 128 snapshotForAccount = string(stmt.Object.ObjName) 129 if currentAccount != sysAccountName && currentAccount != snapshotForAccount { 130 return moerr.NewInternalError(ctx, "only sys tenant can create tenant level snapshot for other tenant") 131 } 132 133 // check account exists or not and get accountId 134 getAccountIdFunc := func(accountName string) (accountId uint64, rtnErr error) { 135 var erArray []ExecResult 136 sql, rtnErr = getSqlForCheckTenant(ctx, accountName) 137 if rtnErr != nil { 138 return 0, rtnErr 139 } 140 bh.ClearExecResultSet() 141 rtnErr = bh.Exec(ctx, sql) 142 if rtnErr != nil { 143 return 0, rtnErr 144 } 145 146 erArray, rtnErr = getResultSet(ctx, bh) 147 if rtnErr != nil { 148 return 0, rtnErr 149 } 150 151 if execResultArrayHasData(erArray) { 152 for i := uint64(0); i < erArray[0].GetRowCount(); i++ { 153 accountId, rtnErr = erArray[0].GetUint64(ctx, i, 0) 154 if rtnErr != nil { 155 return 0, rtnErr 156 } 157 } 158 } else { 159 return 0, moerr.NewInternalError(ctx, "account %s does not exist", accountName) 160 } 161 return accountId, rtnErr 162 } 163 164 // if sys tenant create snapshots for other tenant, get the account id 165 // otherwise, get the account id from tenantInfo 166 if currentAccount == sysAccountName && currentAccount != snapshotForAccount { 167 objId, err = getAccountIdFunc(snapshotForAccount) 168 if err != nil { 169 return err 170 } 171 } else { 172 objId = uint64(tenantInfo.GetTenantID()) 173 } 174 } 175 176 // check snapshot exists or not 177 snapshotName = string(stmt.Name) 178 snapshotExist, err = checkSnapShotExistOrNot(ctx, bh, snapshotName) 179 if err != nil { 180 return err 181 } 182 if snapshotExist { 183 if !stmt.IfNotExists { 184 return moerr.NewInternalError(ctx, "snapshot %s already exists", snapshotName) 185 } else { 186 return nil 187 } 188 } else { 189 // insert record to the system table 190 191 // 1. get snapshot id 192 newUUid, err := uuid.NewV7() 193 if err != nil { 194 return err 195 } 196 snapshotId = newUUid.String() 197 198 // 2. get snapshot ts 199 // ts := ses.proc.TxnOperator.SnapshotTS() 200 // snapshotTs = ts.String() 201 202 sql, err = getSqlForCreateSnapshot(ctx, snapshotId, snapshotName, time.Now().UTC().UnixNano(), snapshotLevel.String(), string(stmt.Object.ObjName), databaseName, tableName, objId) 203 if err != nil { 204 return err 205 } 206 207 err = bh.Exec(ctx, sql) 208 if err != nil { 209 return err 210 } 211 } 212 213 // insert record to the system table 214 215 return err 216 } 217 218 func doDropSnapshot(ctx context.Context, ses *Session, stmt *tree.DropSnapShot) (err error) { 219 var sql string 220 var stageExist bool 221 bh := ses.GetBackgroundExec(ctx) 222 defer bh.Close() 223 224 // check create stage priv 225 // only admin can drop snapshot for himself 226 err = doCheckRole(ctx, ses) 227 if err != nil { 228 return err 229 } 230 231 err = bh.Exec(ctx, "begin;") 232 defer func() { 233 err = finishTxn(ctx, bh, err) 234 }() 235 if err != nil { 236 return err 237 } 238 239 // check stage 240 stageExist, err = checkSnapShotExistOrNot(ctx, bh, string(stmt.Name)) 241 if err != nil { 242 return err 243 } 244 245 if !stageExist { 246 if !stmt.IfExists { 247 return moerr.NewInternalError(ctx, "snapshot %s does not exist", string(stmt.Name)) 248 } else { 249 // do nothing 250 return err 251 } 252 } else { 253 sql = getSqlForDropSnapshot(string(stmt.Name)) 254 err = bh.Exec(ctx, sql) 255 if err != nil { 256 return err 257 } 258 } 259 return err 260 } 261 262 func doRestoreSnapshot(ctx context.Context, ses *Session, stmt *tree.RestoreSnapShot) (err error) { 263 bh := ses.GetBackgroundExec(ctx) 264 defer bh.Close() 265 266 srcAccountName := string(stmt.AccountName) 267 dbName := string(stmt.DatabaseName) 268 tblName := string(stmt.TableName) 269 snapshotName := string(stmt.SnapShotName) 270 toAccountName := string(stmt.ToAccountName) 271 272 // check snapshot 273 snapshot, err := getSnapshotByName(ctx, bh, snapshotName) 274 if err != nil { 275 return err 276 } 277 if snapshot == nil { 278 return moerr.NewInternalError(ctx, "snapshot %s does not exist", snapshotName) 279 } 280 if snapshot.accountName != srcAccountName { 281 return moerr.NewInternalError(ctx, "accountName(%v) does not match snapshot.accountName(%v)", srcAccountName, snapshot.accountName) 282 } 283 284 // default restore to src account 285 toAccountId, err := getAccountId(ctx, bh, snapshot.accountName) 286 if err != nil { 287 return err 288 } 289 290 if len(toAccountName) > 0 { 291 // can't restore to another account if cur account is not sys 292 if !ses.GetTenantInfo().IsSysTenant() { 293 err = moerr.NewInternalError(ctx, "non-sys account can't restore snapshot to another account") 294 return 295 } 296 297 if toAccountName == sysAccountName && snapshot.accountName != sysAccountName { 298 err = moerr.NewInternalError(ctx, "non-sys account's snapshot can't restore to sys account") 299 return 300 } 301 302 if toAccountId, err = getAccountId(ctx, bh, string(stmt.ToAccountName)); err != nil { 303 return err 304 } 305 } 306 307 // restore as a txn 308 if err = bh.Exec(ctx, "begin;"); err != nil { 309 return err 310 } 311 defer func() { 312 err = finishTxn(ctx, bh, err) 313 }() 314 315 // collect views and tables with foreign keys during table restoration 316 var views []*tableInfo 317 var fkTables []*tableInfo 318 fkDeps, err := getFkDeps(ctx, bh, snapshotName, dbName, tblName) 319 if err != nil { 320 return 321 } 322 323 switch stmt.Level { 324 case tree.RESTORELEVELCLUSTER: 325 // TODO 326 case tree.RESTORELEVELACCOUNT: 327 if err = restoreToAccount(ctx, bh, snapshotName, toAccountId, fkDeps, &fkTables, &views); err != nil { 328 return err 329 } 330 case tree.RESTORELEVELDATABASE: 331 if err = restoreToDatabase(ctx, bh, snapshotName, dbName, toAccountId, fkDeps, &fkTables, &views); err != nil { 332 return err 333 } 334 case tree.RESTORELEVELTABLE: 335 if err = restoreToTable(ctx, bh, snapshotName, dbName, tblName, toAccountId, fkDeps, &fkTables, &views); err != nil { 336 return err 337 } 338 } 339 340 if len(fkTables) > 0 { 341 if err = restoreTablesWithFk(ctx, bh, snapshotName, fkDeps, fkTables, toAccountId); err != nil { 342 return 343 } 344 } 345 346 if len(views) > 0 { 347 if err = restoreViews(ctx, ses, bh, snapshotName, views, toAccountId); err != nil { 348 return 349 } 350 } 351 return 352 } 353 354 func restoreToAccount( 355 ctx context.Context, 356 bh BackgroundExec, 357 snapshotName string, 358 toAccountId uint32, 359 fkDeps map[string][]string, 360 fkTables *[]*tableInfo, 361 views *[]*tableInfo) (err error) { 362 logInfof("snapshot", fmt.Sprintf("[%s] start to restore account: %v", snapshotName, toAccountId)) 363 364 var dbNames []string 365 toCtx := defines.AttachAccountId(ctx, toAccountId) 366 367 // delete current dbs 368 if dbNames, err = showDatabases(toCtx, bh, ""); err != nil { 369 return 370 } 371 372 for _, dbName := range dbNames { 373 if needSkipDb(dbName) { 374 logInfof("snapshot", fmt.Sprintf("skip drop db: %v", dbName)) 375 continue 376 } 377 378 if err = bh.Exec(toCtx, fmt.Sprintf("drop database %s", dbName)); err != nil { 379 return 380 } 381 } 382 383 // restore dbs 384 if dbNames, err = showDatabases(ctx, bh, snapshotName); err != nil { 385 return 386 } 387 388 for _, dbName := range dbNames { 389 if err = restoreToDatabase(ctx, bh, snapshotName, dbName, toAccountId, fkDeps, fkTables, views); err != nil { 390 return 391 } 392 } 393 return 394 } 395 396 func restoreToDatabase( 397 ctx context.Context, 398 bh BackgroundExec, 399 snapshotName string, 400 dbName string, 401 toAccountId uint32, 402 fkDeps map[string][]string, 403 fkTables *[]*tableInfo, 404 views *[]*tableInfo) (err error) { 405 logInfof("snapshot", fmt.Sprintf("[%s] start to restore db: %v", snapshotName, dbName)) 406 return restoreToDatabaseOrTable(ctx, bh, snapshotName, dbName, "", toAccountId, fkDeps, fkTables, views) 407 } 408 409 func restoreToTable( 410 ctx context.Context, 411 bh BackgroundExec, 412 snapshotName string, 413 dbName string, 414 tblName string, 415 toAccountId uint32, 416 fkDeps map[string][]string, 417 fkTables *[]*tableInfo, 418 views *[]*tableInfo) (err error) { 419 logInfof("snapshot", fmt.Sprintf("[%s] start to restore table: %v", snapshotName, tblName)) 420 return restoreToDatabaseOrTable(ctx, bh, snapshotName, dbName, tblName, toAccountId, fkDeps, fkTables, views) 421 } 422 423 func restoreToDatabaseOrTable( 424 ctx context.Context, 425 bh BackgroundExec, 426 snapshotName string, 427 dbName string, 428 tblName string, 429 toAccountId uint32, 430 fkDeps map[string][]string, 431 fkTables *[]*tableInfo, 432 views *[]*tableInfo) (err error) { 433 if needSkipDb(dbName) { 434 logInfof("snapshot", fmt.Sprintf("skip restore db: %v", dbName)) 435 return 436 } 437 438 toCtx := defines.AttachAccountId(ctx, toAccountId) 439 restoreToTbl := tblName != "" 440 441 // if restore to db, delete the same name db first 442 if !restoreToTbl { 443 if err = bh.Exec(toCtx, "drop database if exists "+dbName); err != nil { 444 return 445 } 446 } 447 448 if err = bh.Exec(toCtx, "create database if not exists "+dbName); err != nil { 449 return 450 } 451 452 tableInfos, err := getTableInfos(ctx, bh, snapshotName, dbName, tblName) 453 if err != nil { 454 return 455 } 456 457 // if restore to table, expect only one table here 458 if restoreToTbl && len(tableInfos) != 1 { 459 return moerr.NewInternalError(ctx, "find %v tableInfos by name, expect 1", len(tableInfos)) 460 } 461 462 for _, tblInfo := range tableInfos { 463 if needSkipTable(dbName, tblInfo.tblName) { 464 logInfof("snapshot", fmt.Sprintf("skip table: %v.%v", dbName, tblInfo.tblName)) 465 continue 466 } 467 468 // skip table which has foreign keys 469 if _, ok := fkDeps[genKey(dbName, tblInfo.tblName)]; ok { 470 *fkTables = append(*fkTables, tblInfo) 471 continue 472 } 473 474 // skip view 475 if tblInfo.typ == view { 476 *views = append(*views, tblInfo) 477 continue 478 } 479 480 if err = recreateTable(ctx, bh, snapshotName, tblInfo, toAccountId); err != nil { 481 return 482 } 483 } 484 return 485 } 486 487 func restoreTablesWithFk( 488 ctx context.Context, 489 bh BackgroundExec, 490 snapshotName string, 491 fkDeps map[string][]string, 492 fkTables []*tableInfo, 493 toAccountId uint32) (err error) { 494 495 keyTableInfoMap := make(map[string]*tableInfo) 496 g := topsort{next: make(map[string][]string)} 497 for _, tblInfo := range fkTables { 498 key := genKey(tblInfo.dbName, tblInfo.tblName) 499 keyTableInfoMap[key] = tblInfo 500 501 g.addVertex(key) 502 for _, depTbl := range fkDeps[key] { 503 // exclude self constrains 504 if key != depTbl { 505 g.addEdge(depTbl, key) 506 } 507 } 508 } 509 510 // topsort 511 sortedTbls, ok := g.sort() 512 if !ok { 513 return moerr.NewInternalError(ctx, "There is a cycle in dependency graph") 514 } 515 516 // create views 517 for _, key := range sortedTbls { 518 // if not ok, means that table is not in this restoration task, ignore 519 if tblInfo, ok := keyTableInfoMap[key]; ok { 520 logInfof("snapshot", fmt.Sprintf("[%s] start to restore table with fk: %v", snapshotName, tblInfo.tblName)) 521 522 if err = recreateTable(ctx, bh, snapshotName, tblInfo, toAccountId); err != nil { 523 return 524 } 525 } 526 } 527 return 528 } 529 530 func restoreViews( 531 ctx context.Context, 532 ses *Session, 533 bh BackgroundExec, 534 snapshotName string, 535 views []*tableInfo, 536 toAccountId uint32) error { 537 snapshot, err := doResolveSnapshotWithSnapshotName(ctx, ses, snapshotName) 538 if err != nil { 539 return err 540 } 541 542 compCtx := ses.GetTxnCompileCtx() 543 oldSnapshot := compCtx.GetSnapshot() 544 compCtx.SetSnapshot(snapshot) 545 defer func() { 546 compCtx.SetSnapshot(oldSnapshot) 547 }() 548 549 keyTableInfoMap := make(map[string]*tableInfo) 550 g := topsort{next: make(map[string][]string)} 551 for _, view := range views { 552 key := genKey(view.dbName, view.tblName) 553 keyTableInfoMap[key] = view 554 555 stmts, err := parsers.Parse(ctx, dialect.MYSQL, view.createSql, 1, 0) 556 if err != nil { 557 return err 558 } 559 560 // build create sql to find dependent views 561 if _, err = plan.BuildPlan(compCtx, stmts[0], false); err != nil { 562 return err 563 } 564 565 g.addVertex(key) 566 for _, depView := range compCtx.GetViews() { 567 g.addEdge(depView, key) 568 } 569 } 570 571 // topsort 572 sortedViews, ok := g.sort() 573 if !ok { 574 return moerr.NewInternalError(ctx, "There is a cycle in dependency graph") 575 } 576 577 // create views 578 toCtx := defines.AttachAccountId(ctx, toAccountId) 579 for _, key := range sortedViews { 580 // if not ok, means that view is not in this restoration task, ignore 581 if tblInfo, ok := keyTableInfoMap[key]; ok { 582 logInfof("snapshot", fmt.Sprintf("[%s] start to restore view: %v", snapshotName, tblInfo.tblName)) 583 584 if err = bh.Exec(toCtx, "use "+tblInfo.dbName); err != nil { 585 return err 586 } 587 588 if err = bh.Exec(toCtx, "drop view if exists "+tblInfo.tblName); err != nil { 589 return err 590 } 591 592 if err = bh.Exec(toCtx, tblInfo.createSql); err != nil { 593 return err 594 } 595 } 596 } 597 return nil 598 } 599 600 func recreateTable( 601 ctx context.Context, 602 bh BackgroundExec, 603 snapshotName string, 604 tblInfo *tableInfo, 605 toAccountId uint32) (err error) { 606 curAccountId, err := defines.GetAccountId(ctx) 607 if err != nil { 608 return 609 } 610 611 ctx = defines.AttachAccountId(ctx, toAccountId) 612 613 if err = bh.Exec(ctx, fmt.Sprintf("use %s", tblInfo.dbName)); err != nil { 614 return 615 } 616 617 if err = bh.Exec(ctx, fmt.Sprintf("drop table if exists %s", tblInfo.tblName)); err != nil { 618 return 619 } 620 621 // create table 622 if err = bh.Exec(ctx, tblInfo.createSql); err != nil { 623 return 624 } 625 626 // insert data 627 insertIntoSql := fmt.Sprintf(restoreTableDataFmt, tblInfo.dbName, tblInfo.tblName, tblInfo.dbName, tblInfo.tblName, snapshotName) 628 629 if curAccountId == toAccountId { 630 if err = bh.Exec(ctx, insertIntoSql); err != nil { 631 return 632 } 633 } else { 634 if err = bh.ExecRestore(ctx, insertIntoSql, curAccountId, toAccountId); err != nil { 635 return 636 } 637 } 638 return 639 } 640 641 func needSkipDb(dbName string) bool { 642 return slices.Contains(skipDbs, dbName) 643 } 644 645 func needSkipTable(dbName string, tblName string) bool { 646 // TODO determine which tables should be skipped 647 648 if dbName == "information_schema" { 649 return true 650 } 651 652 if dbName == "mo_catalog" { 653 return true 654 } 655 656 return false 657 } 658 659 func checkSnapShotExistOrNot(ctx context.Context, bh BackgroundExec, snapshotName string) (bool, error) { 660 var sql string 661 var erArray []ExecResult 662 var err error 663 sql, err = getSqlForCheckSnapshot(ctx, snapshotName) 664 if err != nil { 665 return false, err 666 } 667 bh.ClearExecResultSet() 668 err = bh.Exec(ctx, sql) 669 if err != nil { 670 return false, err 671 } 672 673 erArray, err = getResultSet(ctx, bh) 674 if err != nil { 675 return false, err 676 } 677 678 if execResultArrayHasData(erArray) { 679 return true, nil 680 } 681 return false, nil 682 } 683 684 func getSnapshotRecords(ctx context.Context, bh BackgroundExec, sql string) ([]*snapshotRecord, error) { 685 var erArray []ExecResult 686 var err error 687 688 bh.ClearExecResultSet() 689 if err = bh.Exec(ctx, sql); err != nil { 690 return nil, err 691 } 692 693 if erArray, err = getResultSet(ctx, bh); err != nil { 694 return nil, err 695 } 696 697 var records []*snapshotRecord 698 if execResultArrayHasData(erArray) { 699 for _, er := range erArray { 700 var record snapshotRecord 701 for row := uint64(0); row < er.GetRowCount(); row++ { 702 if record.snapshotId, err = er.GetString(ctx, row, 0); err != nil { 703 return nil, err 704 } 705 if record.snapshotName, err = er.GetString(ctx, row, 1); err != nil { 706 return nil, err 707 } 708 if record.ts, err = er.GetInt64(ctx, row, 2); err != nil { 709 return nil, err 710 } 711 if record.level, err = er.GetString(ctx, row, 3); err != nil { 712 return nil, err 713 } 714 if record.accountName, err = er.GetString(ctx, row, 4); err != nil { 715 return nil, err 716 } 717 if record.databaseName, err = er.GetString(ctx, row, 5); err != nil { 718 return nil, err 719 } 720 if record.tableName, err = er.GetString(ctx, row, 6); err != nil { 721 return nil, err 722 } 723 if record.objId, err = er.GetUint64(ctx, row, 7); err != nil { 724 return nil, err 725 } 726 } 727 records = append(records, &record) 728 } 729 return records, nil 730 } 731 return nil, err 732 } 733 734 func getSnapshotByName(ctx context.Context, bh BackgroundExec, snapshotName string) (*snapshotRecord, error) { 735 if err := inputNameIsInvalid(ctx, snapshotName); err != nil { 736 return nil, err 737 } 738 739 sql := fmt.Sprintf("%s where sname = '%s'", getSnapshotFormat, snapshotName) 740 if records, err := getSnapshotRecords(ctx, bh, sql); err != nil { 741 return nil, err 742 } else if len(records) != 1 { 743 return nil, moerr.NewInternalError(ctx, "find %v snapshot records by name(%v), expect only 1", len(records), snapshotName) 744 } else { 745 return records[0], nil 746 } 747 } 748 749 func doResolveSnapshotWithSnapshotName(ctx context.Context, ses FeSession, snapshotName string) (snapshot *pbplan.Snapshot, err error) { 750 bh := ses.GetBackgroundExec(ctx) 751 defer bh.Close() 752 753 var record *snapshotRecord 754 if record, err = getSnapshotByName(ctx, bh, snapshotName); err != nil { 755 return 756 } 757 758 if record == nil { 759 err = moerr.NewInternalError(ctx, "snapshot %s does not exist", snapshotName) 760 return 761 } 762 763 var accountId uint32 764 if accountId, err = getAccountId(ctx, bh, record.accountName); err != nil { 765 return 766 } 767 768 return &pbplan.Snapshot{ 769 TS: ×tamp.Timestamp{PhysicalTime: record.ts}, 770 Tenant: &pbplan.SnapshotTenant{ 771 TenantName: record.accountName, 772 TenantID: accountId, 773 }, 774 }, nil 775 } 776 777 func getSqlForCheckSnapshot(ctx context.Context, snapshot string) (string, error) { 778 err := inputNameIsInvalid(ctx, snapshot) 779 if err != nil { 780 return "", err 781 } 782 return fmt.Sprintf(checkSnapshotFormat, snapshot), nil 783 } 784 785 func getSqlForGetSnapshotTsWithSnapshotName(ctx context.Context, snapshot string) (string, error) { 786 err := inputNameIsInvalid(ctx, snapshot) 787 if err != nil { 788 return "", err 789 } 790 return fmt.Sprintf(getSnapshotTsWithSnapshotNameFormat, snapshot), nil 791 } 792 793 func getSqlForCheckSnapshotTs(snapshotTs int64) string { 794 return fmt.Sprintf(checkSnapshotTsFormat, snapshotTs) 795 } 796 797 func getSqlForCreateSnapshot(ctx context.Context, snapshotId, snapshotName string, ts int64, level, accountName, databaseName, tableName string, objectId uint64) (string, error) { 798 err := inputNameIsInvalid(ctx, snapshotName) 799 if err != nil { 800 return "", err 801 } 802 return fmt.Sprintf(insertIntoMoSnapshots, snapshotId, snapshotName, ts, level, accountName, databaseName, tableName, objectId), nil 803 } 804 805 func getSqlForDropSnapshot(snapshotName string) string { 806 return fmt.Sprintf(dropSnapshotFormat, snapshotName) 807 } 808 809 func getStringColsList(ctx context.Context, bh BackgroundExec, sql string, colIndices ...uint64) (ans [][]string, err error) { 810 bh.ClearExecResultSet() 811 if err = bh.Exec(ctx, sql); err != nil { 812 return 813 } 814 815 resultSet, err := getResultSet(ctx, bh) 816 if err != nil { 817 return 818 } 819 820 for _, rs := range resultSet { 821 for row := uint64(0); row < rs.GetRowCount(); row++ { 822 ansRow := make([]string, len(colIndices)) 823 for i := 0; i < len(colIndices); i++ { 824 if ansRow[i], err = rs.GetString(ctx, row, colIndices[i]); err != nil { 825 return nil, err 826 } 827 } 828 ans = append(ans, ansRow) 829 } 830 } 831 return 832 } 833 834 func showDatabases(ctx context.Context, bh BackgroundExec, snapshotName string) ([]string, error) { 835 sql := "show databases" 836 if len(snapshotName) > 0 { 837 sql += fmt.Sprintf(" {snapshot = '%s'}", snapshotName) 838 } 839 840 // cols: dbname 841 colsList, err := getStringColsList(ctx, bh, sql, 0) 842 if err != nil { 843 return nil, err 844 } 845 846 dbNames := make([]string, len(colsList)) 847 for i, cols := range colsList { 848 dbNames[i] = cols[0] 849 } 850 return dbNames, nil 851 } 852 853 func showFullTables(ctx context.Context, bh BackgroundExec, snapshotName string, dbName string, tblName string) ([]*tableInfo, error) { 854 sql := fmt.Sprintf("show full tables from `%s` {snapshot = '%s'}", dbName, snapshotName) 855 if len(tblName) > 0 { 856 sql = fmt.Sprintf("show full tables from `%s` like '%s' {snapshot = '%s'}", dbName, tblName, snapshotName) 857 } 858 859 // cols: table name, table type 860 colsList, err := getStringColsList(ctx, bh, sql, 0, 1) 861 if err != nil { 862 return nil, err 863 } 864 865 ans := make([]*tableInfo, len(colsList)) 866 for i, cols := range colsList { 867 ans[i] = &tableInfo{ 868 dbName: dbName, 869 tblName: cols[0], 870 typ: tableType(cols[1]), 871 } 872 } 873 return ans, nil 874 } 875 876 func getTableInfos(ctx context.Context, bh BackgroundExec, snapshotName string, dbName string, tblName string) ([]*tableInfo, error) { 877 tableInfos, err := showFullTables(ctx, bh, snapshotName, dbName, tblName) 878 if err != nil { 879 return nil, err 880 } 881 882 for _, tblInfo := range tableInfos { 883 if tblInfo.createSql, err = getCreateTableSql(ctx, bh, snapshotName, dbName, tblInfo.tblName); err != nil { 884 return nil, err 885 } 886 } 887 return tableInfos, nil 888 } 889 890 func getCreateTableSql(ctx context.Context, bh BackgroundExec, snapshotName string, dbName string, tblName string) (string, error) { 891 sql := fmt.Sprintf("show create table `%s`.`%s` {snapshot = '%s'}", dbName, tblName, snapshotName) 892 // cols: table_name, create_sql 893 colsList, err := getStringColsList(ctx, bh, sql, 1) 894 if err != nil { 895 return "", nil 896 } 897 if len(colsList) == 0 || len(colsList[0]) == 0 { 898 return "", moerr.NewNoSuchTable(ctx, dbName, tblName) 899 } 900 return colsList[0][0], nil 901 } 902 903 func getAccountId(ctx context.Context, bh BackgroundExec, accountName string) (uint32, error) { 904 ctx = context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID)) 905 sql := getAccountIdNamesSql 906 if len(accountName) > 0 { 907 sql += fmt.Sprintf(" and account_name = '%s'", accountName) 908 } 909 910 bh.ClearExecResultSet() 911 if err := bh.Exec(ctx, sql); err != nil { 912 return 0, err 913 } 914 915 erArray, err := getResultSet(ctx, bh) 916 if err != nil { 917 return 0, err 918 } 919 920 if execResultArrayHasData(erArray) { 921 var accountId int64 922 if accountId, err = erArray[0].GetInt64(ctx, 0, 0); err != nil { 923 return 0, err 924 } 925 return uint32(accountId), nil 926 } 927 928 return 0, moerr.NewInternalError(ctx, "new such account, account name: %v", accountName) 929 } 930 931 func getFkDeps(ctx context.Context, bh BackgroundExec, snapshotName string, dbName string, tblName string) (ans map[string][]string, err error) { 932 sql := fmt.Sprintf("select db_name, table_name, refer_db_name, refer_table_name from mo_catalog.mo_foreign_keys {snapshot = '%s'}", snapshotName) 933 if len(dbName) > 0 { 934 sql = fmt.Sprintf("%s where db_name = '%s'", sql, dbName) 935 if len(tblName) > 0 { 936 sql = fmt.Sprintf("%s and table_name = '%s'", sql, tblName) 937 } 938 } 939 940 bh.ClearExecResultSet() 941 if err = bh.Exec(ctx, sql); err != nil { 942 return 943 } 944 945 resultSet, err := getResultSet(ctx, bh) 946 if err != nil { 947 return nil, err 948 } 949 950 ans = make(map[string][]string) 951 var referDbName, referTblName string 952 953 for _, rs := range resultSet { 954 for row := uint64(0); row < rs.GetRowCount(); row++ { 955 if dbName, err = rs.GetString(ctx, row, 0); err != nil { 956 return 957 } 958 if tblName, err = rs.GetString(ctx, row, 1); err != nil { 959 return 960 } 961 if referDbName, err = rs.GetString(ctx, row, 2); err != nil { 962 return 963 } 964 if referTblName, err = rs.GetString(ctx, row, 3); err != nil { 965 return 966 } 967 968 u := genKey(dbName, tblName) 969 v := genKey(referDbName, referTblName) 970 ans[u] = append(ans[u], v) 971 } 972 } 973 return 974 }