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 }