github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/compiler_context.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  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"sort"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	"go.uber.org/zap"
    29  
    30  	"github.com/matrixorigin/matrixone/pkg/catalog"
    31  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    32  	"github.com/matrixorigin/matrixone/pkg/container/types"
    33  	"github.com/matrixorigin/matrixone/pkg/defines"
    34  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    35  	pb "github.com/matrixorigin/matrixone/pkg/pb/statsinfo"
    36  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    37  	plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan"
    38  	"github.com/matrixorigin/matrixone/pkg/sql/plan/function"
    39  	"github.com/matrixorigin/matrixone/pkg/sql/util"
    40  	v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2"
    41  	"github.com/matrixorigin/matrixone/pkg/vm/engine"
    42  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/blockio"
    43  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common"
    44  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    45  )
    46  
    47  type TxnCompilerContext struct {
    48  	dbName               string
    49  	buildAlterView       bool
    50  	dbOfView, nameOfView string
    51  	sub                  *plan.SubscriptionMeta
    52  	snapshot             *plan2.Snapshot
    53  	views                []string
    54  	//for support explain analyze
    55  	tcw     *TxnComputationWrapper
    56  	execCtx *ExecCtx
    57  	mu      sync.Mutex
    58  }
    59  
    60  var _ plan2.CompilerContext = &TxnCompilerContext{}
    61  
    62  func (tcc *TxnCompilerContext) SetExecCtx(execCtx *ExecCtx) {
    63  	tcc.mu.Lock()
    64  	defer tcc.mu.Unlock()
    65  	tcc.execCtx = execCtx
    66  }
    67  
    68  func (tcc *TxnCompilerContext) GetViews() []string {
    69  	tcc.mu.Lock()
    70  	defer tcc.mu.Unlock()
    71  	return tcc.views
    72  }
    73  
    74  func (tcc *TxnCompilerContext) SetViews(views []string) {
    75  	tcc.mu.Lock()
    76  	defer tcc.mu.Unlock()
    77  	tcc.views = views
    78  }
    79  
    80  func (tcc *TxnCompilerContext) GetSnapshot() *plan2.Snapshot {
    81  	tcc.mu.Lock()
    82  	defer tcc.mu.Unlock()
    83  	return tcc.snapshot
    84  }
    85  
    86  func (tcc *TxnCompilerContext) SetSnapshot(snapshot *plan2.Snapshot) {
    87  	tcc.mu.Lock()
    88  	defer tcc.mu.Unlock()
    89  	tcc.snapshot = snapshot
    90  }
    91  
    92  func (tcc *TxnCompilerContext) ReplacePlan(execPlan *plan.Execute) (*plan.Plan, tree.Statement, error) {
    93  	p, st, _, err := replacePlan(tcc.execCtx.reqCtx, tcc.execCtx.ses.(*Session), tcc.tcw, execPlan)
    94  	return p, st, err
    95  }
    96  
    97  func (tcc *TxnCompilerContext) GetStatsCache() *plan2.StatsCache {
    98  	tcc.mu.Lock()
    99  	defer tcc.mu.Unlock()
   100  	return tcc.execCtx.ses.GetStatsCache()
   101  }
   102  
   103  func InitTxnCompilerContext(db string) *TxnCompilerContext {
   104  	return &TxnCompilerContext{dbName: db}
   105  }
   106  
   107  func (tcc *TxnCompilerContext) SetBuildingAlterView(yesOrNo bool, dbName, viewName string) {
   108  	tcc.mu.Lock()
   109  	defer tcc.mu.Unlock()
   110  	tcc.buildAlterView = yesOrNo
   111  	tcc.dbOfView = dbName
   112  	tcc.nameOfView = viewName
   113  }
   114  
   115  func (tcc *TxnCompilerContext) GetBuildingAlterView() (bool, string, string) {
   116  	tcc.mu.Lock()
   117  	defer tcc.mu.Unlock()
   118  	return tcc.buildAlterView, tcc.dbOfView, tcc.nameOfView
   119  }
   120  
   121  func (tcc *TxnCompilerContext) GetSession() FeSession {
   122  	tcc.mu.Lock()
   123  	defer tcc.mu.Unlock()
   124  	return tcc.execCtx.ses
   125  }
   126  
   127  func (tcc *TxnCompilerContext) GetTxnHandler() *TxnHandler {
   128  	tcc.mu.Lock()
   129  	defer tcc.mu.Unlock()
   130  	return tcc.execCtx.ses.GetTxnHandler()
   131  }
   132  
   133  func (tcc *TxnCompilerContext) GetUserName() string {
   134  	tcc.mu.Lock()
   135  	defer tcc.mu.Unlock()
   136  	return tcc.execCtx.ses.GetUserName()
   137  }
   138  
   139  func (tcc *TxnCompilerContext) SetDatabase(db string) {
   140  	tcc.mu.Lock()
   141  	defer tcc.mu.Unlock()
   142  	tcc.dbName = db
   143  }
   144  
   145  func (tcc *TxnCompilerContext) DefaultDatabase() string {
   146  	tcc.mu.Lock()
   147  	defer tcc.mu.Unlock()
   148  	return tcc.dbName
   149  }
   150  
   151  func (tcc *TxnCompilerContext) GetRootSql() string {
   152  	return tcc.GetSession().GetSql()
   153  }
   154  
   155  func (tcc *TxnCompilerContext) GetAccountId() (uint32, error) {
   156  	return tcc.execCtx.ses.GetAccountId(), nil
   157  }
   158  
   159  func (tcc *TxnCompilerContext) GetContext() context.Context {
   160  	return tcc.execCtx.reqCtx
   161  }
   162  
   163  func (tcc *TxnCompilerContext) DatabaseExists(name string, snapshot plan2.Snapshot) bool {
   164  	var err error
   165  	tempCtx := tcc.execCtx.reqCtx
   166  	txn := tcc.GetTxnHandler().GetTxn()
   167  
   168  	// change txn to snapshot txn
   169  	if plan2.IsSnapshotValid(&snapshot) && snapshot.TS.Less(txn.Txn().SnapshotTS) {
   170  		txn = txn.CloneSnapshotOp(*snapshot.TS)
   171  
   172  		if snapshot.Tenant != nil {
   173  			tempCtx = context.WithValue(tempCtx, defines.TenantIDKey{}, snapshot.Tenant.TenantID)
   174  		}
   175  	}
   176  
   177  	//open database
   178  	ses := tcc.GetSession()
   179  	_, err = tcc.GetTxnHandler().GetStorage().Database(tempCtx, name, txn)
   180  	if err != nil {
   181  		logError(ses, ses.GetDebugString(),
   182  			"Failed to get database",
   183  			zap.String("databaseName", name),
   184  			zap.Error(err))
   185  		return false
   186  	}
   187  
   188  	return true
   189  }
   190  
   191  func (tcc *TxnCompilerContext) GetDatabaseId(dbName string, snapshot plan2.Snapshot) (uint64, error) {
   192  	dbName, _, err := tcc.ensureDatabaseIsNotEmpty(dbName, false, snapshot)
   193  	if err != nil {
   194  		return 0, err
   195  	}
   196  	tempCtx := tcc.execCtx.reqCtx
   197  	txn := tcc.GetTxnHandler().GetTxn()
   198  	// change txn to snapshot txn
   199  
   200  	if plan2.IsSnapshotValid(&snapshot) && snapshot.TS.Less(txn.Txn().SnapshotTS) {
   201  		txn = txn.CloneSnapshotOp(*snapshot.TS)
   202  
   203  		if snapshot.Tenant != nil {
   204  			tempCtx = context.WithValue(tempCtx, defines.TenantIDKey{}, snapshot.Tenant.TenantID)
   205  		}
   206  	}
   207  
   208  	database, err := tcc.GetTxnHandler().GetStorage().Database(tempCtx, dbName, txn)
   209  	if err != nil {
   210  		return 0, err
   211  	}
   212  	databaseId, err := strconv.ParseUint(database.GetDatabaseId(tempCtx), 10, 64)
   213  	if err != nil {
   214  		return 0, moerr.NewInternalError(tempCtx, "The databaseid of '%s' is not a valid number", dbName)
   215  	}
   216  	return databaseId, nil
   217  }
   218  
   219  // getRelation returns the context (maybe updated) and the relation
   220  func (tcc *TxnCompilerContext) getRelation(dbName string, tableName string, sub *plan.SubscriptionMeta, snapshot plan2.Snapshot) (context.Context, engine.Relation, error) {
   221  	dbName, _, err := tcc.ensureDatabaseIsNotEmpty(dbName, false, snapshot)
   222  	if err != nil {
   223  		return nil, nil, err
   224  	}
   225  
   226  	ses := tcc.GetSession()
   227  	txn := tcc.GetTxnHandler().GetTxn()
   228  	tempCtx := tcc.execCtx.reqCtx
   229  
   230  	if plan2.IsSnapshotValid(&snapshot) && snapshot.TS.Less(txn.Txn().SnapshotTS) {
   231  		txn = txn.CloneSnapshotOp(*snapshot.TS)
   232  
   233  		if snapshot.Tenant != nil {
   234  			tempCtx = context.WithValue(tempCtx, defines.TenantIDKey{}, snapshot.Tenant.TenantID)
   235  		}
   236  	}
   237  
   238  	account := ses.GetTenantInfo()
   239  	if isClusterTable(dbName, tableName) {
   240  		//if it is the cluster table in the general account, switch into the sys account
   241  		if account != nil && account.GetTenantID() != sysAccountID {
   242  			tempCtx = defines.AttachAccountId(tempCtx, sysAccountID)
   243  		}
   244  	}
   245  	if sub != nil {
   246  		tempCtx = defines.AttachAccountId(tempCtx, uint32(sub.AccountId))
   247  		dbName = sub.DbName
   248  	}
   249  
   250  	//for system_metrics.metric and system.statement_info,
   251  	//it is special under the no sys account, should switch into the sys account first.
   252  	if dbName == catalog.MO_SYSTEM && tableName == catalog.MO_STATEMENT {
   253  		tempCtx = defines.AttachAccountId(tempCtx, uint32(sysAccountID))
   254  	}
   255  
   256  	if dbName == catalog.MO_SYSTEM_METRICS && (tableName == catalog.MO_METRIC || tableName == catalog.MO_SQL_STMT_CU) {
   257  		tempCtx = defines.AttachAccountId(tempCtx, uint32(sysAccountID))
   258  	}
   259  
   260  	//open database
   261  	db, err := tcc.GetTxnHandler().GetStorage().Database(tempCtx, dbName, txn)
   262  	if err != nil {
   263  		logError(ses, ses.GetDebugString(),
   264  			"Failed to get database",
   265  			zap.String("databaseName", dbName),
   266  			zap.Error(err))
   267  		return nil, nil, err
   268  	}
   269  
   270  	// tableNames, err := db.Relations(ctx)
   271  	// if err != nil {
   272  	// 	return nil, nil, err
   273  	// }
   274  	// logDebugf(ses.GetDebugString(), "dbName %v tableNames %v", dbName, tableNames)
   275  
   276  	//open table
   277  	table, err := db.Relation(tempCtx, tableName, nil)
   278  	if err != nil {
   279  		tmpTable, e := tcc.getTmpRelation(tempCtx, engine.GetTempTableName(dbName, tableName))
   280  		if e != nil {
   281  			logError(ses, ses.GetDebugString(),
   282  				"Failed to get table",
   283  				zap.String("tableName", tableName),
   284  				zap.Error(err))
   285  			return nil, nil, err
   286  		} else {
   287  			table = tmpTable
   288  		}
   289  	}
   290  	return tempCtx, table, nil
   291  }
   292  
   293  func (tcc *TxnCompilerContext) getTmpRelation(ctx context.Context, tableName string) (engine.Relation, error) {
   294  	e := tcc.execCtx.ses.GetTxnHandler().GetStorage()
   295  	txn := tcc.execCtx.ses.GetTxnHandler().GetTxn()
   296  	db, err := e.Database(ctx, defines.TEMPORARY_DBNAME, txn)
   297  	if err != nil {
   298  		logError(tcc.execCtx.ses, tcc.execCtx.ses.GetDebugString(),
   299  			"Failed to get temp database",
   300  			zap.Error(err))
   301  		return nil, err
   302  	}
   303  	table, err := db.Relation(ctx, tableName, nil)
   304  	return table, err
   305  }
   306  
   307  func (tcc *TxnCompilerContext) ensureDatabaseIsNotEmpty(dbName string, checkSub bool, snapshot plan2.Snapshot) (string, *plan.SubscriptionMeta, error) {
   308  	if len(dbName) == 0 {
   309  		dbName = tcc.DefaultDatabase()
   310  	}
   311  	if len(dbName) == 0 {
   312  		return "", nil, moerr.NewNoDB(tcc.GetContext())
   313  	}
   314  	var sub *plan.SubscriptionMeta
   315  	var err error
   316  	if checkSub && !util.DbIsSystemDb(dbName) {
   317  		sub, err = tcc.GetSubscriptionMeta(dbName, snapshot)
   318  		if err != nil {
   319  			return "", nil, err
   320  		}
   321  	}
   322  	return dbName, sub, nil
   323  }
   324  
   325  func (tcc *TxnCompilerContext) ResolveById(tableId uint64, snapshot plan2.Snapshot) (*plan2.ObjectRef, *plan2.TableDef) {
   326  	tempCtx := tcc.execCtx.reqCtx
   327  	txn := tcc.GetTxnHandler().GetTxn()
   328  
   329  	if plan2.IsSnapshotValid(&snapshot) && snapshot.TS.Less(txn.Txn().SnapshotTS) {
   330  		txn = txn.CloneSnapshotOp(*snapshot.TS)
   331  
   332  		if snapshot.Tenant != nil {
   333  			tempCtx = context.WithValue(tempCtx, defines.TenantIDKey{}, snapshot.Tenant.TenantID)
   334  		}
   335  	}
   336  
   337  	dbName, tableName, table, err := tcc.GetTxnHandler().GetStorage().GetRelationById(tempCtx, txn, tableId)
   338  	if err != nil {
   339  		return nil, nil
   340  	}
   341  
   342  	// convert
   343  	obj := &plan2.ObjectRef{
   344  		SchemaName: dbName,
   345  		ObjName:    tableName,
   346  		Obj:        int64(tableId),
   347  	}
   348  	tableDef := table.CopyTableDef(tempCtx)
   349  	return obj, tableDef
   350  }
   351  
   352  func (tcc *TxnCompilerContext) ResolveSubscriptionTableById(tableId uint64, pubmeta *plan.SubscriptionMeta) (*plan2.ObjectRef, *plan2.TableDef) {
   353  	txn := tcc.GetTxnHandler().GetTxn()
   354  
   355  	pubContext := tcc.execCtx.reqCtx
   356  	if pubmeta != nil {
   357  		pubContext = context.WithValue(pubContext, defines.TenantIDKey{}, uint32(pubmeta.AccountId))
   358  	}
   359  
   360  	dbName, tableName, table, err := tcc.GetTxnHandler().GetStorage().GetRelationById(pubContext, txn, tableId)
   361  	if err != nil {
   362  		return nil, nil
   363  	}
   364  
   365  	// convert
   366  	obj := &plan2.ObjectRef{
   367  		SchemaName: dbName,
   368  		ObjName:    tableName,
   369  		Obj:        int64(tableId),
   370  	}
   371  	tableDef := table.CopyTableDef(pubContext)
   372  	return obj, tableDef
   373  }
   374  
   375  func (tcc *TxnCompilerContext) Resolve(dbName string, tableName string, snapshot plan2.Snapshot) (*plan2.ObjectRef, *plan2.TableDef) {
   376  	start := time.Now()
   377  	defer func() {
   378  		v2.TxnStatementResolveDurationHistogram.Observe(time.Since(start).Seconds())
   379  	}()
   380  	dbName, sub, err := tcc.ensureDatabaseIsNotEmpty(dbName, true, snapshot)
   381  	if err != nil {
   382  		return nil, nil
   383  	}
   384  
   385  	ctx, table, err := tcc.getRelation(dbName, tableName, sub, snapshot)
   386  	if err != nil {
   387  		return nil, nil
   388  	}
   389  	tableDef := table.CopyTableDef(ctx)
   390  	if tableDef.IsTemporary {
   391  		tableDef.Name = tableName
   392  	}
   393  	tableDef.DbName = dbName
   394  
   395  	// convert
   396  	var subscriptionName string
   397  	var pubAccountId int32 = -1
   398  	if sub != nil {
   399  		subscriptionName = sub.SubName
   400  		pubAccountId = sub.AccountId
   401  		dbName = sub.DbName
   402  	}
   403  
   404  	obj := &plan2.ObjectRef{
   405  		SchemaName:       dbName,
   406  		ObjName:          tableName,
   407  		Obj:              int64(table.GetTableID(ctx)),
   408  		SubscriptionName: subscriptionName,
   409  	}
   410  	if pubAccountId != -1 {
   411  		obj.PubInfo = &plan.PubInfo{
   412  			TenantId: pubAccountId,
   413  		}
   414  	}
   415  	return obj, tableDef
   416  }
   417  
   418  func (tcc *TxnCompilerContext) ResolveUdf(name string, args []*plan.Expr) (udf *function.Udf, err error) {
   419  	var matchNum int
   420  	var argstr string
   421  	var argTypeStr string
   422  	var sql string
   423  	var erArray []ExecResult
   424  
   425  	start := time.Now()
   426  	defer func() {
   427  		v2.TxnStatementResolveUdfDurationHistogram.Observe(time.Since(start).Seconds())
   428  	}()
   429  	ses := tcc.GetSession()
   430  	ctx := tcc.execCtx.reqCtx
   431  
   432  	err = inputNameIsInvalid(ctx, name)
   433  	if err != nil {
   434  		return nil, err
   435  	}
   436  
   437  	bh := ses.GetBackgroundExec(ctx)
   438  	defer bh.Close()
   439  
   440  	err = bh.Exec(ctx, "begin;")
   441  	defer func() {
   442  		err = finishTxn(ctx, bh, err)
   443  		if execResultArrayHasData(erArray) {
   444  			if matchNum < 1 {
   445  				err = errors.Join(err, moerr.NewInvalidInput(ctx, fmt.Sprintf("No matching function for call to %s(%s)", name, argTypeStr)))
   446  			} else if matchNum > 1 {
   447  				err = errors.Join(err, moerr.NewInvalidInput(ctx, fmt.Sprintf("call to %s(%s) is ambiguous", name, argTypeStr)))
   448  			}
   449  		}
   450  	}()
   451  	if err != nil {
   452  		return nil, err
   453  	}
   454  
   455  	sql = fmt.Sprintf(`select args, body, language, rettype, db, modified_time from mo_catalog.mo_user_defined_function where name = "%s" and db = "%s";`, name, tcc.DefaultDatabase())
   456  	bh.ClearExecResultSet()
   457  	err = bh.Exec(ctx, sql)
   458  	if err != nil {
   459  		return nil, err
   460  	}
   461  
   462  	erArray, err = getResultSet(ctx, bh)
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  
   467  	if execResultArrayHasData(erArray) {
   468  		fromList := make([]types.Type, len(args))
   469  		for i, arg := range args {
   470  			fromList[i] = types.Type{
   471  				Oid:   types.T(arg.Typ.Id),
   472  				Width: arg.Typ.Width,
   473  				Scale: arg.Typ.Scale,
   474  			}
   475  
   476  			argTypeStr += strings.ToLower(fromList[i].String())
   477  			if i+1 != len(args) {
   478  				argTypeStr += ", "
   479  			}
   480  		}
   481  
   482  		// find function which has min type cast cost in reload functions
   483  		type MatchUdf struct {
   484  			Udf      *function.Udf
   485  			Cost     int
   486  			TypeList []types.T
   487  		}
   488  		matchedList := make([]*MatchUdf, 0)
   489  
   490  		for i := uint64(0); i < erArray[0].GetRowCount(); i++ {
   491  			argstr, err = erArray[0].GetString(ctx, i, 0)
   492  			if err != nil {
   493  				return nil, err
   494  			}
   495  			udf = &function.Udf{}
   496  			udf.Body, err = erArray[0].GetString(ctx, i, 1)
   497  			if err != nil {
   498  				return nil, err
   499  			}
   500  			udf.Language, err = erArray[0].GetString(ctx, i, 2)
   501  			if err != nil {
   502  				return nil, err
   503  			}
   504  			udf.RetType, err = erArray[0].GetString(ctx, i, 3)
   505  			if err != nil {
   506  				return nil, err
   507  			}
   508  			udf.Db, err = erArray[0].GetString(ctx, i, 4)
   509  			if err != nil {
   510  				return nil, err
   511  			}
   512  			udf.ModifiedTime, err = erArray[0].GetString(ctx, i, 5)
   513  			if err != nil {
   514  				return nil, err
   515  			}
   516  			udf.ModifiedTime = strings.ReplaceAll(udf.ModifiedTime, " ", "_")
   517  			udf.ModifiedTime = strings.ReplaceAll(udf.ModifiedTime, ":", "-")
   518  			// arg type check
   519  			argList := make([]*function.Arg, 0)
   520  			err = json.Unmarshal([]byte(argstr), &argList)
   521  			if err != nil {
   522  				return nil, err
   523  			}
   524  			if len(argList) != len(args) { // mismatch
   525  				continue
   526  			}
   527  
   528  			toList := make([]types.T, len(args))
   529  			for j := range argList {
   530  				if fromList[j].IsDecimal() && argList[j].Type == "decimal" {
   531  					toList[j] = fromList[j].Oid
   532  				} else {
   533  					toList[j] = types.Types[argList[j].Type]
   534  				}
   535  			}
   536  
   537  			canCast, cost := function.UdfArgTypeMatch(fromList, toList)
   538  			if !canCast { // mismatch
   539  				continue
   540  			}
   541  
   542  			udf.Args = argList
   543  			matchedList = append(matchedList, &MatchUdf{
   544  				Udf:      udf,
   545  				Cost:     cost,
   546  				TypeList: toList,
   547  			})
   548  		}
   549  
   550  		if len(matchedList) == 0 {
   551  			return nil, err
   552  		}
   553  
   554  		sort.Slice(matchedList, func(i, j int) bool {
   555  			return matchedList[i].Cost < matchedList[j].Cost
   556  		})
   557  
   558  		minCost := matchedList[0].Cost
   559  		for _, matchUdf := range matchedList {
   560  			if matchUdf.Cost == minCost {
   561  				matchNum++
   562  			}
   563  		}
   564  
   565  		if matchNum == 1 {
   566  			matchedList[0].Udf.ArgsType = function.UdfArgTypeCast(fromList, matchedList[0].TypeList)
   567  			return matchedList[0].Udf, err
   568  		}
   569  
   570  		return nil, err
   571  	} else {
   572  		return nil, moerr.NewNotSupported(ctx, "function or operator '%s'", name)
   573  	}
   574  }
   575  
   576  func (tcc *TxnCompilerContext) ResolveVariable(varName string, isSystemVar, isGlobalVar bool) (interface{}, error) {
   577  	ctx := tcc.execCtx.reqCtx
   578  
   579  	if ctx.Value(defines.InSp{}) != nil && ctx.Value(defines.InSp{}).(bool) {
   580  		tmpScope := ctx.Value(defines.VarScopeKey{}).(*[]map[string]interface{})
   581  		for i := len(*tmpScope) - 1; i >= 0; i-- {
   582  			curScope := (*tmpScope)[i]
   583  			if val, ok := curScope[strings.ToLower(varName)]; ok {
   584  				return val, nil
   585  			}
   586  		}
   587  	}
   588  
   589  	if isSystemVar {
   590  		if isGlobalVar {
   591  			return tcc.GetSession().GetGlobalSystemVariableValue(ctx, varName)
   592  		} else {
   593  			return tcc.GetSession().GetSessionVar(ctx, varName)
   594  		}
   595  	} else {
   596  		_, val, err := tcc.GetSession().GetUserDefinedVar(varName)
   597  		if val == nil {
   598  			return nil, err
   599  		}
   600  		return val.Value, err
   601  	}
   602  }
   603  
   604  func (tcc *TxnCompilerContext) ResolveAccountIds(accountNames []string) (accountIds []uint32, err error) {
   605  	var sql string
   606  	var erArray []ExecResult
   607  	var targetAccountId uint64
   608  	if len(accountNames) == 0 {
   609  		return []uint32{}, nil
   610  	}
   611  
   612  	dedup := make(map[string]int8)
   613  	for _, name := range accountNames {
   614  		dedup[name] = 1
   615  	}
   616  
   617  	ses := tcc.GetSession()
   618  	ctx := tcc.execCtx.reqCtx
   619  	bh := ses.GetBackgroundExec(ctx)
   620  	defer bh.Close()
   621  
   622  	err = bh.Exec(ctx, "begin;")
   623  	defer func() {
   624  		err = finishTxn(ctx, bh, err)
   625  	}()
   626  	if err != nil {
   627  		return nil, err
   628  	}
   629  
   630  	for name := range dedup {
   631  		sql, err = getSqlForCheckTenant(ctx, name)
   632  		if err != nil {
   633  			return nil, err
   634  		}
   635  		bh.ClearExecResultSet()
   636  		err = bh.Exec(ctx, sql)
   637  		if err != nil {
   638  			return nil, err
   639  		}
   640  
   641  		erArray, err = getResultSet(ctx, bh)
   642  		if err != nil {
   643  			return nil, err
   644  		}
   645  
   646  		if execResultArrayHasData(erArray) {
   647  			for i := uint64(0); i < erArray[0].GetRowCount(); i++ {
   648  				targetAccountId, err = erArray[0].GetUint64(ctx, i, 0)
   649  				if err != nil {
   650  					return nil, err
   651  				}
   652  			}
   653  			accountIds = append(accountIds, uint32(targetAccountId))
   654  		} else {
   655  			return nil, moerr.NewInternalError(ctx, "there is no account %s", name)
   656  		}
   657  	}
   658  	return accountIds, err
   659  }
   660  
   661  func (tcc *TxnCompilerContext) GetPrimaryKeyDef(dbName string, tableName string, snapshot plan2.Snapshot) []*plan2.ColDef {
   662  	dbName, sub, err := tcc.ensureDatabaseIsNotEmpty(dbName, true, snapshot)
   663  	if err != nil {
   664  		return nil
   665  	}
   666  	ctx, relation, err := tcc.getRelation(dbName, tableName, sub, snapshot)
   667  	if err != nil {
   668  		return nil
   669  	}
   670  
   671  	priKeys, err := relation.GetPrimaryKeys(ctx)
   672  	if err != nil {
   673  		return nil
   674  	}
   675  	if len(priKeys) == 0 {
   676  		return nil
   677  	}
   678  
   679  	priDefs := make([]*plan2.ColDef, 0, len(priKeys))
   680  	for _, key := range priKeys {
   681  		priDefs = append(priDefs, &plan2.ColDef{
   682  			Name: key.Name,
   683  			Typ: plan2.Type{
   684  				Id:    int32(key.Type.Oid),
   685  				Width: key.Type.Width,
   686  				Scale: key.Type.Scale,
   687  			},
   688  			Primary: key.Primary,
   689  		})
   690  	}
   691  	return priDefs
   692  }
   693  
   694  func (tcc *TxnCompilerContext) Stats(obj *plan2.ObjectRef, snapshot plan2.Snapshot) (*pb.StatsInfo, error) {
   695  	start := time.Now()
   696  	defer func() {
   697  		v2.TxnStatementStatsDurationHistogram.Observe(time.Since(start).Seconds())
   698  	}()
   699  
   700  	dbName := obj.GetSchemaName()
   701  	checkSub := true
   702  	if obj.PubInfo != nil {
   703  		checkSub = false
   704  	}
   705  	dbName, sub, err := tcc.ensureDatabaseIsNotEmpty(dbName, checkSub, snapshot)
   706  	if err != nil {
   707  		return nil, err
   708  	}
   709  	if !checkSub {
   710  		sub = &plan.SubscriptionMeta{
   711  			AccountId: obj.PubInfo.TenantId,
   712  			DbName:    dbName,
   713  		}
   714  	}
   715  	tableName := obj.GetObjName()
   716  	ctx, table, err := tcc.getRelation(dbName, tableName, sub, snapshot)
   717  	if err != nil {
   718  		return nil, err
   719  	}
   720  	s, needUpdate := tcc.statsInCache(ctx, dbName, table, snapshot)
   721  	if s == nil {
   722  		return nil, nil
   723  	}
   724  	if needUpdate {
   725  		s, err = table.Stats(ctx, true)
   726  		if err != nil {
   727  			return s, err
   728  		}
   729  		if s != nil {
   730  			tcc.UpdateStatsInCache(table.GetTableID(ctx), s)
   731  		}
   732  	}
   733  	return s, nil
   734  }
   735  
   736  func (tcc *TxnCompilerContext) UpdateStatsInCache(tid uint64, s *pb.StatsInfo) {
   737  	tcc.GetStatsCache().SetStatsInfo(tid, s)
   738  }
   739  
   740  // statsInCache get the *pb.StatsInfo from session cache. If the info is nil, just return nil and false,
   741  // else, check if the info needs to be updated.
   742  func (tcc *TxnCompilerContext) statsInCache(ctx context.Context, dbName string, table engine.Relation, snapshot plan2.Snapshot) (*pb.StatsInfo, bool) {
   743  	s := tcc.GetStatsCache().GetStatsInfo(table.GetTableID(ctx), true)
   744  	if s == nil {
   745  		return nil, false
   746  	}
   747  
   748  	var partitionInfo *plan2.PartitionByDef
   749  	engineDefs, err := table.TableDefs(ctx)
   750  	if err != nil {
   751  		return nil, false
   752  	}
   753  	for _, def := range engineDefs {
   754  		if partitionDef, ok := def.(*engine.PartitionDef); ok {
   755  			if partitionDef.Partitioned > 0 {
   756  				p := &plan2.PartitionByDef{}
   757  				err = p.UnMarshalPartitionInfo(([]byte)(partitionDef.Partition))
   758  				if err != nil {
   759  					return nil, false
   760  				}
   761  				partitionInfo = p
   762  			}
   763  		}
   764  	}
   765  	approxNumObjects := 0
   766  	if partitionInfo != nil {
   767  		for _, PartitionTableName := range partitionInfo.PartitionTableNames {
   768  			_, ptable, _ := tcc.getRelation(dbName, PartitionTableName, nil, snapshot)
   769  			approxNumObjects += ptable.ApproxObjectsNum(ctx)
   770  		}
   771  	} else {
   772  		approxNumObjects = table.ApproxObjectsNum(ctx)
   773  	}
   774  	if approxNumObjects == 0 {
   775  		return nil, false
   776  	}
   777  	if s.NeedUpdate(int64(approxNumObjects)) {
   778  		return s, true
   779  	}
   780  	return s, false
   781  }
   782  
   783  func (tcc *TxnCompilerContext) GetProcess() *process.Process {
   784  	tcc.mu.Lock()
   785  	defer tcc.mu.Unlock()
   786  	return tcc.execCtx.proc
   787  }
   788  
   789  func (tcc *TxnCompilerContext) GetQueryResultMeta(uuid string) ([]*plan.ColDef, string, error) {
   790  	proc := tcc.execCtx.proc
   791  	// get file size
   792  	path := catalog.BuildQueryResultMetaPath(proc.SessionInfo.Account, uuid)
   793  	// read meta's meta
   794  	reader, err := blockio.NewFileReader(proc.FileService, path)
   795  	if err != nil {
   796  		return nil, "", err
   797  	}
   798  	idxs := make([]uint16, 2)
   799  	idxs[0] = catalog.COLUMNS_IDX
   800  	idxs[1] = catalog.RESULT_PATH_IDX
   801  	// read meta's data
   802  	bats, release, err := reader.LoadAllColumns(tcc.execCtx.reqCtx, idxs, common.DefaultAllocator)
   803  	if err != nil {
   804  		if moerr.IsMoErrCode(err, moerr.ErrFileNotFound) {
   805  			return nil, "", moerr.NewResultFileNotFound(tcc.execCtx.reqCtx, makeResultMetaPath(proc.SessionInfo.Account, uuid))
   806  		}
   807  		return nil, "", err
   808  	}
   809  	defer func() {
   810  		if release != nil {
   811  			release()
   812  		}
   813  	}()
   814  	// cols
   815  	vec := bats[0].Vecs[0]
   816  	def := vec.GetStringAt(0)
   817  	r := &plan.ResultColDef{}
   818  	if err = r.Unmarshal([]byte(def)); err != nil {
   819  		return nil, "", err
   820  	}
   821  	// paths
   822  	vec = bats[0].Vecs[1]
   823  	str := vec.GetStringAt(0)
   824  	return r.ResultCols, str, nil
   825  }
   826  
   827  func (tcc *TxnCompilerContext) GetSubscriptionMeta(dbName string, snapshot plan2.Snapshot) (*plan.SubscriptionMeta, error) {
   828  	tempCtx := tcc.execCtx.reqCtx
   829  	txn := tcc.GetTxnHandler().GetTxn()
   830  
   831  	if plan2.IsSnapshotValid(&snapshot) && snapshot.TS.Less(txn.Txn().SnapshotTS) {
   832  		txn = txn.CloneSnapshotOp(*snapshot.TS)
   833  
   834  		if snapshot.Tenant != nil {
   835  			tempCtx = context.WithValue(tempCtx, defines.TenantIDKey{}, snapshot.Tenant.TenantID)
   836  		}
   837  	}
   838  
   839  	sub, err := getSubscriptionMeta(tempCtx, dbName, tcc.GetSession(), txn)
   840  	if err != nil {
   841  		return nil, err
   842  	}
   843  	return sub, nil
   844  }
   845  
   846  func (tcc *TxnCompilerContext) CheckSubscriptionValid(subName, accName, pubName string) error {
   847  	_, err := checkSubscriptionValidCommon(tcc.GetContext(), tcc.GetSession(), subName, accName, pubName)
   848  	return err
   849  }
   850  
   851  func (tcc *TxnCompilerContext) SetQueryingSubscription(meta *plan.SubscriptionMeta) {
   852  	tcc.mu.Lock()
   853  	defer tcc.mu.Unlock()
   854  	tcc.sub = meta
   855  }
   856  
   857  func (tcc *TxnCompilerContext) GetQueryingSubscription() *plan.SubscriptionMeta {
   858  	tcc.mu.Lock()
   859  	defer tcc.mu.Unlock()
   860  	return tcc.sub
   861  }
   862  
   863  func (tcc *TxnCompilerContext) IsPublishing(dbName string) (bool, error) {
   864  	return isDbPublishing(tcc.GetContext(), dbName, tcc.GetSession())
   865  }
   866  
   867  // makeResultMetaPath gets query result meta path
   868  func makeResultMetaPath(accountName string, statementId string) string {
   869  	return fmt.Sprintf("query_result_meta/%s_%s.blk", accountName, statementId)
   870  }
   871  
   872  func (tcc *TxnCompilerContext) ResolveSnapshotWithSnapshotName(snapshotName string) (*plan2.Snapshot, error) {
   873  	tenantCtx := tcc.GetContext()
   874  	if snapshot := tcc.GetSnapshot(); snapshot != nil && snapshot.GetTenant() != nil {
   875  		tenantCtx = defines.AttachAccount(tenantCtx, snapshot.Tenant.TenantID, GetAdminUserId(), GetAccountAdminRoleId())
   876  	}
   877  	return doResolveSnapshotWithSnapshotName(tenantCtx, tcc.GetSession(), snapshotName)
   878  }
   879  
   880  func (tcc *TxnCompilerContext) CheckTimeStampValid(ts int64) (bool, error) {
   881  	return checkTimeStampValid(tcc.GetContext(), tcc.GetSession(), ts)
   882  }