github.com/matrixorigin/matrixone@v0.7.0/pkg/frontend/cmd_executor.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/matrixorigin/matrixone/pkg/common/moerr"
    22  	"github.com/matrixorigin/matrixone/pkg/logutil"
    23  	"github.com/matrixorigin/matrixone/pkg/util/metric"
    24  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    25  )
    26  
    27  // CmdExecutor handle the command from the client
    28  type CmdExecutor interface {
    29  	SetSession(*Session)
    30  
    31  	GetSession() *Session
    32  
    33  	// ExecRequest execute the request and get the response
    34  	ExecRequest(context.Context, *Session, *Request) (*Response, error)
    35  
    36  	//SetCancelFunc saves a cancel function for active request.
    37  	SetCancelFunc(context.CancelFunc)
    38  
    39  	// CancelRequest cancels the active request
    40  	CancelRequest()
    41  
    42  	Close()
    43  }
    44  
    45  type CmdExecutorImpl struct {
    46  	CmdExecutor
    47  }
    48  
    49  type doComQueryFunc func(context.Context, string) error
    50  
    51  type stmtExecStatus int
    52  
    53  const (
    54  	stmtExecSuccess stmtExecStatus = iota
    55  	stmtExecFail
    56  )
    57  
    58  // StmtExecutor represents the single statement execution.
    59  // it is also independent of the protocol
    60  type StmtExecutor interface {
    61  	ComputationWrapper
    62  
    63  	// GetStatus returns the execution status
    64  	GetStatus() stmtExecStatus
    65  
    66  	// SetStatus sets the execution status
    67  	SetStatus(err error)
    68  
    69  	// Setup does preparation
    70  	Setup(ctx context.Context, ses *Session) error
    71  
    72  	// VerifyPrivilege ensures the user can execute this statement
    73  	VerifyPrivilege(ctx context.Context, ses *Session) error
    74  
    75  	// VerifyTxn checks the restriction of the transaction
    76  	VerifyTxn(ctx context.Context, ses *Session) error
    77  
    78  	// ResponseBeforeExec responses the client before the execution starts
    79  	ResponseBeforeExec(ctx context.Context, ses *Session) error
    80  
    81  	// ExecuteImpl runs the concrete logic of the statement. every statement has its implementation
    82  	ExecuteImpl(ctx context.Context, ses *Session) error
    83  
    84  	// ResponseAfterExec responses the client after the execution ends
    85  	ResponseAfterExec(ctx context.Context, ses *Session) error
    86  
    87  	// CommitOrRollbackTxn commits or rollbacks the transaction based on the status
    88  	CommitOrRollbackTxn(ctx context.Context, ses *Session) error
    89  
    90  	// Close does clean
    91  	Close(ctx context.Context, ses *Session) error
    92  }
    93  
    94  var _ StmtExecutor = &baseStmtExecutor{}
    95  var _ StmtExecutor = &statusStmtExecutor{}
    96  var _ StmtExecutor = &resultSetStmtExecutor{}
    97  
    98  // Execute runs the statement executor
    99  func Execute(ctx context.Context, ses *Session, proc *process.Process, stmtExec StmtExecutor, beginInstant time.Time, envStmt string, useEnv bool) error {
   100  	var err, err2 error
   101  	var cmpBegin, runBegin time.Time
   102  	ctx = RecordStatement(ctx, ses, proc, stmtExec, beginInstant, envStmt, useEnv)
   103  	err = stmtExec.Setup(ctx, ses)
   104  	if err != nil {
   105  		goto handleRet
   106  	}
   107  
   108  	err = stmtExec.VerifyPrivilege(ctx, ses)
   109  	if err != nil {
   110  		goto handleRet
   111  	}
   112  
   113  	err = stmtExec.VerifyTxn(ctx, ses)
   114  	if err != nil {
   115  		goto handleRet
   116  	}
   117  
   118  	ses.GetTxnCompileCtx().SetQueryType(TXN_DEFAULT)
   119  
   120  	if err = stmtExec.SetDatabaseName(ses.GetDatabaseName()); err != nil {
   121  		goto handleRet
   122  	}
   123  
   124  	cmpBegin = time.Now()
   125  
   126  	//TODO: selfhandle statements do not need to compile
   127  	if _, err = stmtExec.Compile(ctx, ses, ses.GetOutputCallback()); err != nil {
   128  		goto handleRet
   129  	}
   130  
   131  	logutil.Infof("time of Exec.Build : %s", time.Since(cmpBegin).String())
   132  
   133  	err = stmtExec.ResponseBeforeExec(ctx, ses)
   134  	if err != nil {
   135  		goto handleRet
   136  	}
   137  
   138  	runBegin = time.Now()
   139  
   140  	err = stmtExec.ExecuteImpl(ctx, ses)
   141  	if err != nil {
   142  		goto handleRet
   143  	}
   144  
   145  	logutil.Infof("time of Exec.Run : %s", time.Since(runBegin).String())
   146  
   147  	_ = stmtExec.RecordExecPlan(ctx)
   148  
   149  handleRet:
   150  	stmtExec.SetStatus(err)
   151  	err2 = stmtExec.CommitOrRollbackTxn(ctx, ses)
   152  	if err2 != nil {
   153  		return err2
   154  	}
   155  
   156  	err2 = stmtExec.ResponseAfterExec(ctx, ses)
   157  	if err2 != nil {
   158  		return err2
   159  	}
   160  
   161  	err2 = stmtExec.Close(ctx, ses)
   162  	if err2 != nil {
   163  		return err2
   164  	}
   165  	return err
   166  }
   167  
   168  // baseStmtExecutor the base class for the statement execution
   169  type baseStmtExecutor struct {
   170  	ComputationWrapper
   171  	tenantName string
   172  	status     stmtExecStatus
   173  	err        error
   174  }
   175  
   176  func (bse *baseStmtExecutor) GetStatus() stmtExecStatus {
   177  	return bse.status
   178  }
   179  
   180  func (bse *baseStmtExecutor) SetStatus(err error) {
   181  	bse.err = err
   182  	bse.status = stmtExecSuccess
   183  	if err != nil {
   184  		bse.status = stmtExecFail
   185  	}
   186  
   187  }
   188  
   189  func (bse *baseStmtExecutor) CommitOrRollbackTxn(ctx context.Context, ses *Session) error {
   190  	var txnErr error
   191  	stmt := bse.GetAst()
   192  	tenant := bse.tenantName
   193  	incStatementCounter(tenant, stmt)
   194  	if bse.GetStatus() == stmtExecSuccess {
   195  		txnErr = ses.TxnCommitSingleStatement(stmt)
   196  		if txnErr != nil {
   197  			incTransactionErrorsCounter(tenant, metric.SQLTypeCommit)
   198  			logStatementStatus(ctx, ses, stmt, fail, txnErr)
   199  			return txnErr
   200  		}
   201  		logStatementStatus(ctx, ses, stmt, success, nil)
   202  	} else {
   203  		incStatementErrorsCounter(tenant, stmt)
   204  		/*
   205  			Cases    | set Autocommit = 1/0 | BEGIN statement |
   206  			---------------------------------------------------
   207  			Case1      1                       Yes
   208  			Case2      1                       No
   209  			Case3      0                       Yes
   210  			Case4      0                       No
   211  			---------------------------------------------------
   212  			update error message in Case1,Case3,Case4.
   213  		*/
   214  		if ses.InMultiStmtTransactionMode() && ses.InActiveTransaction() {
   215  			ses.SetOptionBits(OPTION_ATTACH_ABORT_TRANSACTION_ERROR)
   216  		}
   217  		logutil.Error(bse.err.Error())
   218  		txnErr = ses.TxnRollbackSingleStatement(stmt)
   219  		if txnErr != nil {
   220  			incTransactionErrorsCounter(tenant, metric.SQLTypeRollback)
   221  			logStatementStatus(ctx, ses, stmt, fail, txnErr)
   222  			return txnErr
   223  		}
   224  		logStatementStatus(ctx, ses, stmt, fail, bse.err)
   225  	}
   226  	return nil
   227  }
   228  
   229  func (bse *baseStmtExecutor) ExecuteImpl(ctx context.Context, ses *Session) error {
   230  	return bse.Run(0)
   231  }
   232  
   233  func (bse *baseStmtExecutor) Setup(ctx context.Context, ses *Session) error {
   234  	ses.SetMysqlResultSet(&MysqlResultSet{})
   235  	return nil
   236  }
   237  
   238  func (bse *baseStmtExecutor) Close(ctx context.Context, ses *Session) error {
   239  	ses.SetMysqlResultSet(nil)
   240  	return nil
   241  }
   242  
   243  func (bse *baseStmtExecutor) VerifyPrivilege(ctx context.Context, ses *Session) error {
   244  	var err error
   245  	bse.tenantName = sysAccountName
   246  	//skip PREPARE statement here
   247  	if ses.GetTenantInfo() != nil && !IsPrepareStatement(bse.GetAst()) {
   248  		bse.tenantName = ses.GetTenantInfo().GetTenant()
   249  		err = authenticateUserCanExecuteStatement(ctx, ses, bse.GetAst())
   250  		if err != nil {
   251  			return err
   252  		}
   253  	}
   254  	return err
   255  }
   256  
   257  func (bse *baseStmtExecutor) VerifyTxn(ctx context.Context, ses *Session) error {
   258  	var err error
   259  	var can bool
   260  	/*
   261  			if it is in an active or multi-statement transaction, we check the type of the statement.
   262  			Then we decide that if we can execute the statement.
   263  
   264  		If we check the active transaction, it will generate the case below.
   265  		case:
   266  		set autocommit = 0;  <- no active transaction
   267  		                     <- no active transaction
   268  		drop table test1;    <- no active transaction, no error
   269  		                     <- has active transaction
   270  		drop table test1;    <- has active transaction, error
   271  		                     <- has active transaction
   272  	*/
   273  	if ses.InActiveTransaction() {
   274  		stmt := bse.GetAst()
   275  		can, err = StatementCanBeExecutedInUncommittedTransaction(ses, stmt)
   276  		if err != nil {
   277  			return err
   278  		}
   279  		if !can {
   280  			//is ddl statement
   281  			if IsDDL(stmt) {
   282  				return moerr.NewInternalError(ctx, onlyCreateStatementErrorInfo())
   283  			} else if IsAdministrativeStatement(stmt) {
   284  				return moerr.NewInternalError(ctx, administrativeCommandIsUnsupportedInTxnErrorInfo())
   285  			} else if IsParameterModificationStatement(stmt) {
   286  				return moerr.NewInternalError(ctx, parameterModificationInTxnErrorInfo())
   287  			} else {
   288  				return moerr.NewInternalError(ctx, unclassifiedStatementInUncommittedTxnErrorInfo())
   289  			}
   290  		}
   291  	}
   292  	return err
   293  }
   294  
   295  func (bse *baseStmtExecutor) ResponseBeforeExec(ctx context.Context, ses *Session) error {
   296  	return nil
   297  }
   298  
   299  func (bse *baseStmtExecutor) ResponseAfterExec(ctx context.Context, ses *Session) error {
   300  	var err, retErr error
   301  	if bse.GetStatus() == stmtExecSuccess {
   302  		resp := NewOkResponse(bse.GetAffectedRows(), 0, 0, 0, int(COM_QUERY), "")
   303  		if err = ses.GetMysqlProtocol().SendResponse(ctx, resp); err != nil {
   304  			retErr = moerr.NewInternalError(ctx, "routine send response failed. error:%v ", err)
   305  			logStatementStatus(ctx, ses, bse.GetAst(), fail, retErr)
   306  			return retErr
   307  		}
   308  	}
   309  	return nil
   310  }