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: ×tamp.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 }