github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/computation_wrapper.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  	"time"
    20  
    21  	"github.com/google/uuid"
    22  	"github.com/mohae/deepcopy"
    23  
    24  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    25  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    26  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    27  	"github.com/matrixorigin/matrixone/pkg/container/types"
    28  	"github.com/matrixorigin/matrixone/pkg/defines"
    29  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    30  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    31  	"github.com/matrixorigin/matrixone/pkg/sql/compile"
    32  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect"
    33  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    34  	plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan"
    35  	"github.com/matrixorigin/matrixone/pkg/sql/util"
    36  	"github.com/matrixorigin/matrixone/pkg/txn/clock"
    37  	"github.com/matrixorigin/matrixone/pkg/txn/storage/memorystorage"
    38  	txnTrace "github.com/matrixorigin/matrixone/pkg/txn/trace"
    39  	util2 "github.com/matrixorigin/matrixone/pkg/util"
    40  	"github.com/matrixorigin/matrixone/pkg/util/trace"
    41  	"github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace"
    42  	"github.com/matrixorigin/matrixone/pkg/util/trace/impl/motrace/statistic"
    43  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    44  )
    45  
    46  var (
    47  	_ ComputationWrapper = &TxnComputationWrapper{}
    48  	_ ComputationWrapper = &NullComputationWrapper{}
    49  )
    50  
    51  type NullComputationWrapper struct {
    52  	*TxnComputationWrapper
    53  }
    54  
    55  func InitNullComputationWrapper(ses *Session, stmt tree.Statement, proc *process.Process) *NullComputationWrapper {
    56  	return &NullComputationWrapper{
    57  		TxnComputationWrapper: InitTxnComputationWrapper(ses, stmt, proc),
    58  	}
    59  }
    60  
    61  func (ncw *NullComputationWrapper) GetAst() tree.Statement {
    62  	return ncw.stmt
    63  }
    64  
    65  func (ncw *NullComputationWrapper) GetColumns(context.Context) ([]interface{}, error) {
    66  	return []interface{}{}, nil
    67  }
    68  
    69  func (ncw *NullComputationWrapper) Compile(any any, fill func(*batch.Batch) error) (interface{}, error) {
    70  	return nil, nil
    71  }
    72  
    73  func (ncw *NullComputationWrapper) RecordExecPlan(ctx context.Context) error {
    74  	return nil
    75  }
    76  
    77  func (ncw *NullComputationWrapper) GetUUID() []byte {
    78  	return ncw.uuid[:]
    79  }
    80  
    81  func (ncw *NullComputationWrapper) Run(ts uint64) (*util2.RunResult, error) {
    82  	return nil, nil
    83  }
    84  
    85  func (ncw *NullComputationWrapper) GetLoadTag() bool {
    86  	return false
    87  }
    88  func (ncw *NullComputationWrapper) Clear() {
    89  
    90  }
    91  func (ncw *NullComputationWrapper) Plan() *plan.Plan {
    92  	return nil
    93  }
    94  func (ncw *NullComputationWrapper) ResetPlanAndStmt(tree.Statement) {
    95  
    96  }
    97  
    98  func (ncw *NullComputationWrapper) Free() {
    99  	ncw.Clear()
   100  }
   101  
   102  type TxnComputationWrapper struct {
   103  	stmt      tree.Statement
   104  	plan      *plan2.Plan
   105  	proc      *process.Process
   106  	ses       FeSession
   107  	compile   *compile.Compile
   108  	runResult *util2.RunResult
   109  
   110  	ifIsExeccute bool
   111  	uuid         uuid.UUID
   112  	//holds values of params in the PREPARE
   113  	paramVals []any
   114  }
   115  
   116  func InitTxnComputationWrapper(ses FeSession, stmt tree.Statement, proc *process.Process) *TxnComputationWrapper {
   117  	uuid, _ := uuid.NewV7()
   118  	return &TxnComputationWrapper{
   119  		stmt: stmt,
   120  		proc: proc,
   121  		ses:  ses,
   122  		uuid: uuid,
   123  	}
   124  }
   125  
   126  func (cwft *TxnComputationWrapper) Plan() *plan.Plan {
   127  	return cwft.plan
   128  }
   129  
   130  func (cwft *TxnComputationWrapper) ResetPlanAndStmt(stmt tree.Statement) {
   131  	cwft.plan = nil
   132  	cwft.freeStmt()
   133  	cwft.stmt = stmt
   134  }
   135  
   136  func (cwft *TxnComputationWrapper) GetAst() tree.Statement {
   137  	return cwft.stmt
   138  }
   139  
   140  func (cwft *TxnComputationWrapper) Free() {
   141  	cwft.freeStmt()
   142  	cwft.Clear()
   143  }
   144  
   145  func (cwft *TxnComputationWrapper) freeStmt() {
   146  	if cwft.stmt != nil {
   147  		if !cwft.ifIsExeccute {
   148  			cwft.stmt.Free()
   149  			cwft.stmt = nil
   150  		}
   151  	}
   152  }
   153  
   154  func (cwft *TxnComputationWrapper) Clear() {
   155  	cwft.plan = nil
   156  	cwft.proc = nil
   157  	cwft.ses = nil
   158  	cwft.compile = nil
   159  	cwft.runResult = nil
   160  }
   161  
   162  func (cwft *TxnComputationWrapper) GetProcess() *process.Process {
   163  	return cwft.proc
   164  }
   165  
   166  func (cwft *TxnComputationWrapper) GetColumns(ctx context.Context) ([]interface{}, error) {
   167  	var err error
   168  	cols := plan2.GetResultColumnsFromPlan(cwft.plan)
   169  	switch cwft.GetAst().(type) {
   170  	case *tree.ShowColumns:
   171  		if len(cols) == 7 {
   172  			cols = []*plan2.ColDef{
   173  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Field"},
   174  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Type"},
   175  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Null"},
   176  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Key"},
   177  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Default"},
   178  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Extra"},
   179  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Comment"},
   180  			}
   181  		} else {
   182  			cols = []*plan2.ColDef{
   183  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Field"},
   184  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Type"},
   185  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Collation"},
   186  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Null"},
   187  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Key"},
   188  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Default"},
   189  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Extra"},
   190  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Privileges"},
   191  				{Typ: plan2.Type{Id: int32(types.T_char)}, Name: "Comment"},
   192  			}
   193  		}
   194  	}
   195  	columns := make([]interface{}, len(cols))
   196  	for i, col := range cols {
   197  		c := new(MysqlColumn)
   198  		c.SetName(col.Name)
   199  		c.SetOrgName(col.Name)
   200  		c.SetTable(col.TblName)
   201  		c.SetOrgTable(col.TblName)
   202  		c.SetAutoIncr(col.Typ.AutoIncr)
   203  		c.SetSchema(col.DbName)
   204  		err = convertEngineTypeToMysqlType(ctx, types.T(col.Typ.Id), c)
   205  		if err != nil {
   206  			return nil, err
   207  		}
   208  		setColFlag(c)
   209  		setColLength(c, col.Typ.Width)
   210  		setCharacter(c)
   211  
   212  		// For binary/varbinary with mysql_type_varchar.Change the charset.
   213  		if types.T(col.Typ.Id) == types.T_binary || types.T(col.Typ.Id) == types.T_varbinary {
   214  			c.SetCharset(0x3f)
   215  		}
   216  
   217  		c.SetDecimal(col.Typ.Scale)
   218  		convertMysqlTextTypeToBlobType(c)
   219  		columns[i] = c
   220  	}
   221  	return columns, err
   222  }
   223  
   224  func (cwft *TxnComputationWrapper) GetClock() clock.Clock {
   225  	rt := runtime.ProcessLevelRuntime()
   226  	return rt.Clock()
   227  }
   228  
   229  func (cwft *TxnComputationWrapper) GetServerStatus() uint16 {
   230  	return uint16(cwft.ses.GetTxnHandler().GetServerStatus())
   231  }
   232  
   233  func (cwft *TxnComputationWrapper) Compile(any any, fill func(*batch.Batch) error) (interface{}, error) {
   234  	var originSQL string
   235  	var span trace.Span
   236  	execCtx := any.(*ExecCtx)
   237  	execCtx.reqCtx, span = trace.Start(execCtx.reqCtx, "TxnComputationWrapper.Compile",
   238  		trace.WithKind(trace.SpanKindStatement))
   239  	defer span.End(trace.WithStatementExtra(cwft.ses.GetTxnId(), cwft.ses.GetStmtId(), cwft.ses.GetSqlOfStmt()))
   240  
   241  	var err error
   242  	defer RecordStatementTxnID(execCtx.reqCtx, cwft.ses)
   243  	if cwft.ses.GetTxnHandler().HasTempEngine() {
   244  		updateTempStorageInCtx(execCtx, cwft.proc, cwft.ses.GetTxnHandler().GetTempStorage())
   245  	}
   246  
   247  	cacheHit := cwft.plan != nil
   248  	if !cacheHit {
   249  		cwft.plan, err = buildPlan(execCtx.reqCtx, cwft.ses, cwft.ses.GetTxnCompileCtx(), cwft.stmt)
   250  	} else if cwft.ses != nil && cwft.ses.GetTenantInfo() != nil && !cwft.ses.IsBackgroundSession() {
   251  		var accId uint32
   252  		accId, err = defines.GetAccountId(execCtx.reqCtx)
   253  		if err != nil {
   254  			return nil, err
   255  		}
   256  		cwft.ses.SetAccountId(accId)
   257  		err = authenticateCanExecuteStatementAndPlan(execCtx.reqCtx, cwft.ses.(*Session), cwft.stmt, cwft.plan)
   258  	}
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	if !cwft.ses.IsBackgroundSession() {
   263  		cwft.ses.SetPlan(cwft.plan)
   264  		if ids := isResultQuery(cwft.plan); ids != nil {
   265  			if err = checkPrivilege(ids, execCtx.reqCtx, cwft.ses.(*Session)); err != nil {
   266  				return nil, err
   267  			}
   268  		}
   269  	}
   270  
   271  	if _, ok := cwft.stmt.(*tree.Execute); ok {
   272  		executePlan := cwft.plan.GetDcl().GetExecute()
   273  		plan, stmt, sql, err := replacePlan(execCtx.reqCtx, cwft.ses.(*Session), cwft, executePlan)
   274  		if err != nil {
   275  			return nil, err
   276  		}
   277  		originSQL = sql
   278  		cwft.plan = plan
   279  
   280  		cwft.stmt.Free()
   281  		// reset plan & stmt
   282  		cwft.stmt = stmt
   283  		cwft.ifIsExeccute = true
   284  		// reset some special stmt for execute statement
   285  		switch cwft.stmt.(type) {
   286  		case *tree.ShowTableStatus:
   287  			cwft.ses.SetShowStmtType(ShowTableStatus)
   288  			cwft.ses.SetData(nil)
   289  		case *tree.SetVar, *tree.ShowVariables, *tree.ShowErrors, *tree.ShowWarnings,
   290  			*tree.CreateAccount, *tree.AlterAccount, *tree.DropAccount:
   291  			return nil, nil
   292  		}
   293  
   294  		//check privilege
   295  		/* prepare not need check privilege
   296  		   err = authenticateUserCanExecutePrepareOrExecute(requestCtx, cwft.ses, prepareStmt.PrepareStmt, newPlan)
   297  		   if err != nil {
   298  		   	return nil, err
   299  		   }
   300  		*/
   301  	}
   302  
   303  	addr := ""
   304  	if len(getGlobalPu().ClusterNodes) > 0 {
   305  		addr = getGlobalPu().ClusterNodes[0].Addr
   306  	}
   307  	cwft.proc.Ctx = execCtx.reqCtx
   308  	cwft.proc.FileService = getGlobalPu().FileService
   309  
   310  	var tenant string
   311  	tInfo := cwft.ses.GetTenantInfo()
   312  	if tInfo != nil {
   313  		tenant = tInfo.GetTenant()
   314  	}
   315  
   316  	stats := statistic.StatsInfoFromContext(execCtx.reqCtx)
   317  	stats.CompileStart()
   318  	defer stats.CompileEnd()
   319  	cwft.compile = compile.NewCompile(
   320  		addr,
   321  		cwft.ses.GetDatabaseName(),
   322  		cwft.ses.GetSql(),
   323  		tenant,
   324  		cwft.ses.GetUserName(),
   325  		execCtx.reqCtx,
   326  		cwft.ses.GetTxnHandler().GetStorage(),
   327  		cwft.proc,
   328  		cwft.stmt,
   329  		cwft.ses.GetIsInternal(),
   330  		deepcopy.Copy(cwft.ses.getCNLabels()).(map[string]string),
   331  		getStatementStartAt(execCtx.reqCtx),
   332  	)
   333  	defer func() {
   334  		if err != nil {
   335  			cwft.compile.Release()
   336  		}
   337  	}()
   338  	cwft.compile.SetBuildPlanFunc(func() (*plan2.Plan, error) {
   339  		plan, err := buildPlan(execCtx.reqCtx, cwft.ses, cwft.ses.GetTxnCompileCtx(), cwft.stmt)
   340  		if err != nil {
   341  			return nil, err
   342  		}
   343  		if plan.IsPrepare {
   344  			_, _, err = plan2.ResetPreparePlan(cwft.ses.GetTxnCompileCtx(), plan)
   345  		}
   346  		return plan, err
   347  	})
   348  
   349  	if _, ok := cwft.stmt.(*tree.ExplainAnalyze); ok {
   350  		fill = func(bat *batch.Batch) error { return nil }
   351  	}
   352  	err = cwft.compile.Compile(execCtx.reqCtx, cwft.plan, fill)
   353  	if err != nil {
   354  		return nil, err
   355  	}
   356  	// check if it is necessary to initialize the temporary engine
   357  	if !cwft.ses.GetTxnHandler().HasTempEngine() && cwft.compile.NeedInitTempEngine() {
   358  		// 0. init memory-non-dist storage
   359  		err = cwft.ses.GetTxnHandler().CreateTempStorage(cwft.GetClock())
   360  		if err != nil {
   361  			return nil, err
   362  		}
   363  
   364  		// temporary storage is passed through Ctx
   365  		updateTempStorageInCtx(execCtx, cwft.proc, cwft.ses.GetTxnHandler().GetTempStorage())
   366  
   367  		// 1. init memory-non-dist engine
   368  		cwft.ses.GetTxnHandler().CreateTempEngine()
   369  		tempEngine := cwft.ses.GetTxnHandler().GetTempEngine()
   370  
   371  		// 2. bind the temporary engine to the session and txnHandler
   372  		cwft.compile.SetTempEngine(tempEngine, cwft.ses.GetTxnHandler().GetTempStorage())
   373  
   374  		// 3. init temp-db to store temporary relations
   375  		txnOp2 := cwft.ses.GetTxnHandler().GetTxn()
   376  		err = tempEngine.Create(execCtx.reqCtx, defines.TEMPORARY_DBNAME, txnOp2)
   377  		if err != nil {
   378  			return nil, err
   379  		}
   380  	}
   381  	cwft.compile.SetOriginSQL(originSQL)
   382  	return cwft.compile, err
   383  }
   384  
   385  func updateTempStorageInCtx(execCtx *ExecCtx, proc *process.Process, tempStorage *memorystorage.Storage) {
   386  	if execCtx != nil && execCtx.reqCtx != nil {
   387  		execCtx.reqCtx = attachValue(execCtx.reqCtx, defines.TemporaryTN{}, tempStorage)
   388  	}
   389  	if proc != nil && proc.Ctx != nil {
   390  		proc.Ctx = attachValue(proc.Ctx, defines.TemporaryTN{}, tempStorage)
   391  	}
   392  }
   393  
   394  func (cwft *TxnComputationWrapper) RecordExecPlan(ctx context.Context) error {
   395  	if stm := motrace.StatementFromContext(ctx); stm != nil {
   396  		waitActiveCost := time.Duration(0)
   397  		if handler := cwft.ses.GetTxnHandler(); handler.InActiveTxn() {
   398  			txn := handler.GetTxn()
   399  			if txn != nil {
   400  				waitActiveCost = txn.GetWaitActiveCost()
   401  			}
   402  		}
   403  		stm.SetSerializableExecPlan(NewJsonPlanHandler(ctx, stm, cwft.plan, WithWaitActiveCost(waitActiveCost)))
   404  	}
   405  	return nil
   406  }
   407  
   408  func (cwft *TxnComputationWrapper) GetUUID() []byte {
   409  	return cwft.uuid[:]
   410  }
   411  
   412  func (cwft *TxnComputationWrapper) Run(ts uint64) (*util2.RunResult, error) {
   413  	runResult, err := cwft.compile.Run(ts)
   414  	cwft.compile.Release()
   415  	cwft.runResult = runResult
   416  	cwft.compile = nil
   417  	return runResult, err
   418  }
   419  
   420  func (cwft *TxnComputationWrapper) GetLoadTag() bool {
   421  	return cwft.plan.GetQuery().GetLoadTag()
   422  }
   423  
   424  func appendStatementAt(ctx context.Context, value time.Time) context.Context {
   425  	return context.WithValue(ctx, defines.StartTS{}, value)
   426  }
   427  
   428  func getStatementStartAt(ctx context.Context) time.Time {
   429  	v := ctx.Value(defines.StartTS{})
   430  	if v == nil {
   431  		return time.Now()
   432  	}
   433  	return v.(time.Time)
   434  }
   435  
   436  // replacePlan replaces the plan of the EXECUTE by the plan generated by
   437  // the PREPARE and setups the params for the plan.
   438  func replacePlan(reqCtx context.Context, ses *Session, cwft *TxnComputationWrapper, execPlan *plan.Execute) (*plan.Plan, tree.Statement, string, error) {
   439  	originSQL := ""
   440  	stmtName := execPlan.GetName()
   441  	prepareStmt, err := ses.GetPrepareStmt(reqCtx, stmtName)
   442  	if err != nil {
   443  		return nil, nil, originSQL, err
   444  	}
   445  	if txnTrace.GetService().Enabled(txnTrace.FeatureTraceTxn) {
   446  		originSQL = tree.String(prepareStmt.PrepareStmt, dialect.MYSQL)
   447  	}
   448  	preparePlan := prepareStmt.PreparePlan.GetDcl().GetPrepare()
   449  
   450  	// TODO check if schema change, obj.Obj is zero all the time in 0.6
   451  	for _, obj := range preparePlan.GetSchemas() {
   452  		newObj, newTableDef := ses.txnCompileCtx.Resolve(obj.SchemaName, obj.ObjName, plan2.Snapshot{TS: &timestamp.Timestamp{}})
   453  		if newObj == nil {
   454  			return nil, nil, originSQL, moerr.NewInternalError(reqCtx, "table '%s' in prepare statement '%s' does not exist anymore", obj.ObjName, stmtName)
   455  		}
   456  		if newObj.Obj != obj.Obj || newTableDef.Version != uint32(obj.Server) {
   457  			return nil, nil, originSQL, moerr.NewInternalError(reqCtx, "table '%s' has been changed, please reset prepare statement '%s'", obj.ObjName, stmtName)
   458  		}
   459  	}
   460  
   461  	// The default count is 1. Setting it to 2 ensures that memory will not be reclaimed.
   462  	//  Convenient to reuse memory next time
   463  	if prepareStmt.InsertBat != nil {
   464  		prepareStmt.InsertBat.SetCnt(1000) // we will make sure :  when retry in lock error, we will not clean up this batch
   465  		cwft.proc.SetPrepareBatch(prepareStmt.InsertBat)
   466  		cwft.proc.SetPrepareExprList(prepareStmt.exprList)
   467  	}
   468  	numParams := len(preparePlan.ParamTypes)
   469  	if prepareStmt.params != nil && prepareStmt.params.Length() > 0 { // use binary protocol
   470  		if prepareStmt.params.Length() != numParams {
   471  			return nil, nil, originSQL, moerr.NewInvalidInput(reqCtx, "Incorrect arguments to EXECUTE")
   472  		}
   473  		cwft.proc.SetPrepareParams(prepareStmt.params)
   474  	} else if len(execPlan.Args) > 0 {
   475  		if len(execPlan.Args) != numParams {
   476  			return nil, nil, originSQL, moerr.NewInvalidInput(reqCtx, "Incorrect arguments to EXECUTE")
   477  		}
   478  		params := cwft.proc.GetVector(types.T_text.ToType())
   479  		paramVals := make([]any, numParams)
   480  		for i, arg := range execPlan.Args {
   481  			exprImpl := arg.Expr.(*plan.Expr_V)
   482  			param, err := cwft.proc.GetResolveVariableFunc()(exprImpl.V.Name, exprImpl.V.System, exprImpl.V.Global)
   483  			if err != nil {
   484  				return nil, nil, originSQL, err
   485  			}
   486  			if param == nil {
   487  				return nil, nil, originSQL, moerr.NewInvalidInput(reqCtx, "Incorrect arguments to EXECUTE")
   488  			}
   489  			err = util.AppendAnyToStringVector(cwft.proc, param, params)
   490  			if err != nil {
   491  				return nil, nil, originSQL, err
   492  			}
   493  			paramVals[i] = param
   494  		}
   495  		cwft.proc.SetPrepareParams(params)
   496  		cwft.paramVals = paramVals
   497  	} else {
   498  		if numParams > 0 {
   499  			return nil, nil, originSQL, moerr.NewInvalidInput(reqCtx, "Incorrect arguments to EXECUTE")
   500  		}
   501  	}
   502  	return preparePlan.Plan, prepareStmt.PrepareStmt, originSQL, nil
   503  }