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: &timestamp.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  }