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 }