github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/function/func_mo.go (about) 1 // Copyright 2021 - 2022 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 function 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/matrixorigin/matrixone/pkg/catalog" 26 "github.com/matrixorigin/matrixone/pkg/common/moerr" 27 "github.com/matrixorigin/matrixone/pkg/container/bytejson" 28 "github.com/matrixorigin/matrixone/pkg/container/types" 29 "github.com/matrixorigin/matrixone/pkg/container/vector" 30 "github.com/matrixorigin/matrixone/pkg/defines" 31 "github.com/matrixorigin/matrixone/pkg/logutil" 32 "github.com/matrixorigin/matrixone/pkg/pb/plan" 33 "github.com/matrixorigin/matrixone/pkg/sql/plan/function/functionUtil" 34 "github.com/matrixorigin/matrixone/pkg/vm/engine" 35 "github.com/matrixorigin/matrixone/pkg/vm/process" 36 "go.uber.org/zap" 37 ) 38 39 const ( 40 AllColumns = "*" 41 ) 42 43 // XXX Porting mo functions to function2. 44 // Mo function unit tests are not ported, because it is too heavy and does not test enough cases. 45 // Mo functions are better tested with bvt. 46 47 // MoTableRows returns an estimated row number of a table. 48 func MoTableRows(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) (err error) { 49 rs := vector.MustFunctionResult[int64](result) 50 dbs := vector.GenerateFunctionStrParameter(ivecs[0]) 51 tbls := vector.GenerateFunctionStrParameter(ivecs[1]) 52 53 // XXX WTF 54 e := proc.Ctx.Value(defines.EngineKey{}).(engine.Engine) 55 if proc.TxnOperator == nil { 56 return moerr.NewInternalError(proc.Ctx, "MoTableRows: txn operator is nil") 57 } 58 txn := proc.TxnOperator 59 60 var accountId uint32 61 var accSwitched bool 62 // XXX old code starts a new transaction. why? 63 for i := uint64(0); i < uint64(length); i++ { 64 if accSwitched { 65 accSwitched = false 66 proc.Ctx = defines.AttachAccountId(proc.Ctx, accountId) 67 } 68 69 db, dbnull := dbs.GetStrValue(i) 70 tbl, tblnull := tbls.GetStrValue(i) 71 if dbnull || tblnull { 72 if err := rs.Append(0, true); err != nil { 73 return err 74 } 75 } else { 76 var rel engine.Relation 77 dbStr := functionUtil.QuickBytesToStr(db) 78 tblStr := functionUtil.QuickBytesToStr(tbl) 79 80 if isClusterTable(dbStr, tblStr) { 81 //if it is the cluster table in the general account, switch into the sys account 82 accountId, err = defines.GetAccountId(proc.Ctx) 83 if err != nil { 84 return err 85 } 86 if accountId != uint32(sysAccountID) { 87 accSwitched = true 88 proc.Ctx = defines.AttachAccountId(proc.Ctx, uint32(sysAccountID)) 89 } 90 } 91 ctx := proc.Ctx 92 var dbo engine.Database 93 dbo, err = e.Database(ctx, dbStr, txn) 94 if err != nil { 95 if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) { 96 var buf bytes.Buffer 97 for j := uint64(0); j < uint64(length); j++ { 98 db2, _ := dbs.GetStrValue(j) 99 tbl2, _ := tbls.GetStrValue(j) 100 101 dbStr2 := functionUtil.QuickBytesToStr(db2) 102 tblStr2 := functionUtil.QuickBytesToStr(tbl2) 103 104 buf.WriteString(fmt.Sprintf("%s-%s; ", dbStr2, tblStr2)) 105 } 106 107 logutil.Errorf(fmt.Sprintf("db not found when mo_table_size: %s-%s, extra: %s", dbStr, tblStr, buf.String())) 108 return moerr.NewInvalidArgNoCtx("db not found when mo_table_size", fmt.Sprintf("%s-%s", dbStr, tblStr)) 109 } 110 return err 111 } 112 rel, err = dbo.Relation(ctx, tblStr, nil) 113 if err != nil { 114 return err 115 } 116 117 // get the table definition information and check whether the current table is a partition table 118 var engineDefs []engine.TableDef 119 engineDefs, err = rel.TableDefs(ctx) 120 if err != nil { 121 return err 122 } 123 var partitionInfo *plan.PartitionByDef 124 for _, def := range engineDefs { 125 if partitionDef, ok := def.(*engine.PartitionDef); ok { 126 if partitionDef.Partitioned > 0 { 127 p := &plan.PartitionByDef{} 128 err = p.UnMarshalPartitionInfo(([]byte)(partitionDef.Partition)) 129 if err != nil { 130 return err 131 } 132 partitionInfo = p 133 } 134 } 135 } 136 137 var rows uint64 138 139 // check if the current table is partitioned 140 if partitionInfo != nil { 141 var prel engine.Relation 142 var prows uint64 143 // for partition table, the table rows is equal to the sum of the partition tables. 144 for _, partitionTable := range partitionInfo.PartitionTableNames { 145 prel, err = dbo.Relation(ctx, partitionTable, nil) 146 if err != nil { 147 return err 148 } 149 prows, err = prel.Rows(ctx) 150 if err != nil { 151 return err 152 } 153 rows += prows 154 } 155 } else { 156 if rows, err = rel.Rows(ctx); err != nil { 157 return err 158 } 159 } 160 161 if err = rs.Append(int64(rows), false); err != nil { 162 return err 163 } 164 } 165 } 166 return nil 167 } 168 169 // MoTableSize returns an estimated size of a table. 170 func MoTableSize(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) (err error) { 171 rs := vector.MustFunctionResult[int64](result) 172 dbs := vector.GenerateFunctionStrParameter(ivecs[0]) 173 tbls := vector.GenerateFunctionStrParameter(ivecs[1]) 174 175 e := proc.Ctx.Value(defines.EngineKey{}).(engine.Engine) 176 if proc.TxnOperator == nil { 177 return moerr.NewInternalError(proc.Ctx, "MoTableSize: txn operator is nil") 178 } 179 txn := proc.TxnOperator 180 181 var accountId uint32 182 // XXX old code starts a new transaction. why? 183 for i := uint64(0); i < uint64(length); i++ { 184 foolCtx := proc.Ctx 185 186 db, dbnull := dbs.GetStrValue(i) 187 tbl, tblnull := tbls.GetStrValue(i) 188 if dbnull || tblnull { 189 if err = rs.Append(0, true); err != nil { 190 return err 191 } 192 } else { 193 var rel engine.Relation 194 dbStr := functionUtil.QuickBytesToStr(db) 195 tblStr := functionUtil.QuickBytesToStr(tbl) 196 197 if isClusterTable(dbStr, tblStr) { 198 //if it is the cluster table in the general account, switch into the sys account 199 accountId, err = defines.GetAccountId(foolCtx) 200 if err != nil { 201 return err 202 } 203 if accountId != uint32(sysAccountID) { 204 foolCtx = defines.AttachAccountId(foolCtx, uint32(sysAccountID)) 205 } 206 } 207 208 var dbo engine.Database 209 dbo, err = e.Database(foolCtx, dbStr, txn) 210 if err != nil { 211 if moerr.IsMoErrCode(err, moerr.OkExpectedEOB) { 212 var buf bytes.Buffer 213 for j := uint64(0); j < uint64(length); j++ { 214 db2, _ := dbs.GetStrValue(j) 215 tbl2, _ := tbls.GetStrValue(j) 216 217 dbStr2 := functionUtil.QuickBytesToStr(db2) 218 tblStr2 := functionUtil.QuickBytesToStr(tbl2) 219 220 buf.WriteString(fmt.Sprintf("%s#%s; ", dbStr2, tblStr2)) 221 } 222 223 originalAccId, _ := defines.GetAccountId(proc.Ctx) 224 attachedAccId, _ := defines.GetAccountId(foolCtx) 225 226 logutil.Errorf( 227 fmt.Sprintf("db not found when mo_table_size: %s#%s, acc: %d-%d, extra: %s", 228 dbStr, tblStr, attachedAccId, originalAccId, buf.String())) 229 return moerr.NewInvalidArgNoCtx("db not found when mo_table_size", fmt.Sprintf("%s-%s", dbStr, tblStr)) 230 } 231 return err 232 } 233 rel, err = dbo.Relation(foolCtx, tblStr, nil) 234 if err != nil { 235 return err 236 } 237 238 var oSize, iSize uint64 239 if oSize, err = originalTableSize(foolCtx, dbo, rel); err != nil { 240 return err 241 } 242 if iSize, err = indexesTableSize(foolCtx, dbo, rel); err != nil { 243 return err 244 } 245 246 if err = rs.Append(int64(oSize+iSize), false); err != nil { 247 return err 248 } 249 } 250 } 251 return nil 252 } 253 254 func originalTableSize(ctx context.Context, db engine.Database, rel engine.Relation) (size uint64, err error) { 255 return getTableSize(ctx, db, rel) 256 } 257 258 func getTableSize(ctx context.Context, db engine.Database, rel engine.Relation) (size uint64, err error) { 259 // get the table definition information and check whether the current table is a partition table 260 var engineDefs []engine.TableDef 261 engineDefs, err = rel.TableDefs(ctx) 262 if err != nil { 263 return 0, err 264 } 265 var partitionInfo *plan.PartitionByDef 266 for _, def := range engineDefs { 267 if partitionDef, ok := def.(*engine.PartitionDef); ok { 268 if partitionDef.Partitioned > 0 { 269 p := &plan.PartitionByDef{} 270 err = p.UnMarshalPartitionInfo(([]byte)(partitionDef.Partition)) 271 if err != nil { 272 return 0, err 273 } 274 partitionInfo = p 275 } 276 } 277 } 278 279 // check if the current table is partitioned 280 if partitionInfo != nil { 281 var prel engine.Relation 282 var psize uint64 283 // for partition table, the table size is equal to the sum of the partition tables. 284 for _, partitionTable := range partitionInfo.PartitionTableNames { 285 prel, err = db.Relation(ctx, partitionTable, nil) 286 if err != nil { 287 return 0, err 288 } 289 if psize, err = prel.Size(ctx, AllColumns); err != nil { 290 return 0, err 291 } 292 size += psize 293 } 294 } else { 295 if size, err = rel.Size(ctx, AllColumns); err != nil { 296 return 0, err 297 } 298 } 299 300 return size, nil 301 } 302 303 func indexesTableSize(ctx context.Context, db engine.Database, rel engine.Relation) (totalSize uint64, err error) { 304 var irel engine.Relation 305 var size uint64 306 for _, idef := range rel.GetTableDef(ctx).Indexes { 307 if irel, err = db.Relation(ctx, idef.IndexTableName, nil); err != nil { 308 logutil.Info("indexesTableSize->Relation", 309 zap.String("originTable", rel.GetTableName()), 310 zap.String("indexTableName", idef.IndexTableName), 311 zap.Error(err)) 312 continue 313 } 314 315 if size, err = getTableSize(ctx, db, irel); err != nil { 316 logutil.Info("indexesTableSize->getTableSize", 317 zap.String("originTable", rel.GetTableName()), 318 zap.String("indexTableName", idef.IndexTableName), 319 zap.Error(err)) 320 continue 321 } 322 323 totalSize += size 324 } 325 326 // this is a quick fix for the issue of the indexTableName is empty. 327 // the empty indexTableName causes the `SQL parser err: table "" does not exist` err and 328 // then the mo_table_size call will fail. 329 // this fix does not fix the issue but only avoids the failure caused by it. 330 err = nil 331 return totalSize, err 332 } 333 334 // MoTableColMax return the max value of the column 335 func MoTableColMax(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error { 336 return moTableColMaxMinImpl("mo_table_col_max", ivecs, result, proc, length) 337 } 338 339 // MoTableColMax return the max value of the column 340 func MoTableColMin(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error { 341 return moTableColMaxMinImpl("mo_table_col_min", ivecs, result, proc, length) 342 } 343 344 func moTableColMaxMinImpl(fnName string, parameters []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error { 345 e, ok := proc.Ctx.Value(defines.EngineKey{}).(engine.Engine) 346 if !ok || proc.TxnOperator == nil { 347 return moerr.NewInternalError(proc.Ctx, "MoTableColMaxMin: txn operator is nil") 348 } 349 txn := proc.TxnOperator 350 351 dbNames := vector.GenerateFunctionStrParameter(parameters[0]) 352 tableNames := vector.GenerateFunctionStrParameter(parameters[1]) 353 columnNames := vector.GenerateFunctionStrParameter(parameters[2]) 354 355 minMaxIdx := 0 356 if fnName == "mo_table_col_max" { 357 minMaxIdx = 1 358 } 359 360 var getValueFailed bool 361 rs := vector.MustFunctionResult[types.Varlena](result) 362 for i := uint64(0); i < uint64(length); i++ { 363 db, null1 := dbNames.GetStrValue(i) 364 table, null2 := tableNames.GetStrValue(i) 365 column, null3 := columnNames.GetStrValue(i) 366 if null1 || null2 || null3 { 367 rs.AppendMustNull() 368 } else { 369 dbStr := functionUtil.QuickBytesToStr(db) 370 tableStr := functionUtil.QuickBytesToStr(table) 371 columnStr := functionUtil.QuickBytesToStr(column) 372 373 // Magic code. too confused. 374 if tableStr == "mo_database" || tableStr == "mo_tables" || tableStr == "mo_columns" || tableStr == "sys_async_task" { 375 return moerr.NewInvalidInput(proc.Ctx, "%s has bad input table %s", fnName, tableStr) 376 } 377 if columnStr == "__mo_rowid" { 378 return moerr.NewInvalidInput(proc.Ctx, "%s has bad input column %s", fnName, columnStr) 379 } 380 381 if isClusterTable(dbStr, tableStr) { 382 //if it is the cluster table in the general account, switch into the sys account 383 accountId, err := defines.GetAccountId(proc.Ctx) 384 if err != nil { 385 return err 386 } 387 if accountId != uint32(sysAccountID) { 388 proc.Ctx = defines.AttachAccountId(proc.Ctx, uint32(sysAccountID)) 389 } 390 } 391 ctx := proc.Ctx 392 393 db, err := e.Database(ctx, dbStr, txn) 394 if err != nil { 395 return err 396 } 397 398 if db.IsSubscription(ctx) { 399 // get sub info 400 var sub *plan.SubscriptionMeta 401 if sub, err = proc.SessionInfo.SqlHelper.GetSubscriptionMeta(dbStr); err != nil { 402 return err 403 } 404 405 // replace with pub account id 406 ctx = defines.AttachAccountId(ctx, uint32(sysAccountID)) 407 // replace with real dbname(sub.DbName) 408 if db, err = e.Database(ctx, sub.DbName, txn); err != nil { 409 return err 410 } 411 } 412 413 rel, err := db.Relation(ctx, tableStr, nil) 414 if err != nil { 415 return err 416 } 417 tableColumns, err := rel.TableColumns(ctx) 418 if err != nil { 419 return err 420 } 421 422 ranges, err := rel.Ranges(ctx, nil) 423 if err != nil { 424 return err 425 } 426 427 if ranges.Len() == 0 { 428 getValueFailed = true 429 } else if ranges.Len() == 1 && engine.IsMemtable(ranges.GetBytes(0)) { 430 getValueFailed = true 431 } else { 432 // BUGļ¼ if user delete the max or min value within the same txn, the result will be wrong. 433 tValues, _, er := rel.MaxAndMinValues(ctx) 434 if er != nil { 435 return er 436 } 437 438 // BUG: if user drop the col and add it back with the same name within the same txn, the result will be wrong. 439 for j := range tableColumns { 440 if tableColumns[j].Name == columnStr { 441 strval := getValueInStr(tValues[j][minMaxIdx]) 442 if err = rs.AppendMustBytesValue(functionUtil.QuickStrToBytes(strval)); err != nil { 443 return err 444 } 445 getValueFailed = false 446 break 447 } 448 } 449 } 450 if getValueFailed { 451 rs.AppendMustNull() 452 } 453 } 454 } 455 return nil 456 } 457 458 func getValueInStr(value any) string { 459 switch v := value.(type) { 460 case bool: 461 if v { 462 return "true" 463 } else { 464 return "false" 465 } 466 case uint8: 467 return strconv.FormatUint(uint64(v), 10) 468 case uint16: 469 return strconv.FormatUint(uint64(v), 10) 470 case uint32: 471 return strconv.FormatUint(uint64(v), 10) 472 case uint64: 473 return strconv.FormatUint(uint64(v), 10) 474 case int8: 475 return strconv.FormatInt(int64(v), 10) 476 case int16: 477 return strconv.FormatInt(int64(v), 10) 478 case int32: 479 return strconv.FormatInt(int64(v), 10) 480 case int64: 481 return strconv.FormatInt(int64(v), 10) 482 case float32: 483 return strconv.FormatFloat(float64(v), 'f', -1, 32) 484 case float64: 485 return strconv.FormatFloat(v, 'f', -1, 32) 486 case string: 487 return v 488 case []byte: 489 return string(v) 490 case []float32: 491 // Used by zonemap Min,Max 492 // Used by MO_TABLE_COL_MAX 493 return types.ArrayToString[float32](v) 494 case []float64: 495 return types.ArrayToString[float64](v) 496 case int: 497 return strconv.FormatInt(int64(v), 10) 498 case uint: 499 return strconv.FormatUint(uint64(v), 10) 500 case types.Date: 501 return v.String() 502 case types.Time: 503 return v.String() 504 case types.Datetime: 505 return v.String() 506 case types.Timestamp: 507 return v.String() 508 case bytejson.ByteJson: 509 return v.String() 510 case types.Uuid: 511 return v.ToString() 512 case types.Decimal64: 513 return v.Format(0) 514 case types.Decimal128: 515 return v.Format(0) 516 default: 517 return "" 518 } 519 } 520 521 func isClusterTable(dbName, name string) bool { 522 if dbName == moCatalog { 523 //if it is neither among the tables nor the index table, 524 //it is the cluster table. 525 if _, ok := predefinedTables[name]; !ok && !isIndexTable(name) { 526 return true 527 } 528 } 529 return false 530 } 531 532 func isIndexTable(name string) bool { 533 return strings.HasPrefix(name, catalog.IndexTableNamePrefix) 534 } 535 536 const ( 537 moCatalog = "mo_catalog" 538 sysAccountID = 0 539 ) 540 541 var ( 542 predefinedTables = map[string]int8{ 543 "mo_database": 0, 544 "mo_tables": 0, 545 "mo_columns": 0, 546 "mo_account": 0, 547 "mo_user": 0, 548 "mo_role": 0, 549 "mo_user_grant": 0, 550 "mo_role_grant": 0, 551 "mo_role_privs": 0, 552 "mo_user_defined_function": 0, 553 "mo_stored_procedure": 0, 554 "mo_mysql_compatibility_mode": 0, 555 catalog.MOAutoIncrTable: 0, 556 "mo_indexes": 0, 557 "mo_pubs": 0, 558 "mo_stages": 0, 559 "mo_snapshots": 0, 560 } 561 ) 562 563 // CastIndexToValue returns enum type index according to the value 564 func CastIndexToValue(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error { 565 rs := vector.MustFunctionResult[types.Varlena](result) 566 typeEnums := vector.GenerateFunctionStrParameter(ivecs[0]) 567 indexs := vector.GenerateFunctionFixedTypeParameter[uint16](ivecs[1]) 568 569 for i := uint64(0); i < uint64(length); i++ { 570 typeEnum, typeEnumNull := typeEnums.GetStrValue(i) 571 indexVal, indexnull := indexs.GetValue(i) 572 if typeEnumNull || indexnull { 573 if err := rs.AppendBytes(nil, true); err != nil { 574 return err 575 } 576 } else { 577 typeEnumVal := functionUtil.QuickBytesToStr(typeEnum) 578 var enumVlaue string 579 580 enumVlaue, err := types.ParseEnumIndex(typeEnumVal, indexVal) 581 if err != nil { 582 return err 583 } 584 585 if err = rs.AppendBytes([]byte(enumVlaue), false); err != nil { 586 return err 587 } 588 } 589 } 590 return nil 591 } 592 593 // CastValueToIndex returns enum type index according to the value 594 func CastValueToIndex(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error { 595 rs := vector.MustFunctionResult[uint16](result) 596 typeEnums := vector.GenerateFunctionStrParameter(ivecs[0]) 597 enumValues := vector.GenerateFunctionStrParameter(ivecs[1]) 598 599 for i := uint64(0); i < uint64(length); i++ { 600 typeEnum, typeEnumNull := typeEnums.GetStrValue(i) 601 enumValue, enumValNull := enumValues.GetStrValue(i) 602 if typeEnumNull || enumValNull { 603 if err := rs.Append(0, true); err != nil { 604 return err 605 } 606 } else { 607 typeEnumVal := functionUtil.QuickBytesToStr(typeEnum) 608 enumStr := functionUtil.QuickBytesToStr(enumValue) 609 610 var index uint16 611 index, err := types.ParseEnum(typeEnumVal, enumStr) 612 if err != nil { 613 return err 614 } 615 616 if err = rs.Append(index, false); err != nil { 617 return err 618 } 619 } 620 } 621 return nil 622 } 623 624 // CastIndexValueToIndex returns enum type index according to the index value 625 func CastIndexValueToIndex(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error { 626 rs := vector.MustFunctionResult[uint16](result) 627 typeEnums := vector.GenerateFunctionStrParameter(ivecs[0]) 628 enumIndexValues := vector.GenerateFunctionFixedTypeParameter[uint16](ivecs[1]) 629 630 for i := uint64(0); i < uint64(length); i++ { 631 typeEnum, typeEnumNull := typeEnums.GetStrValue(i) 632 enumValueIndex, enumValNull := enumIndexValues.GetValue(i) 633 if typeEnumNull || enumValNull { 634 if err := rs.Append(0, true); err != nil { 635 return err 636 } 637 } else { 638 typeEnumVal := functionUtil.QuickBytesToStr(typeEnum) 639 var index uint16 640 641 index, err := types.ParseEnumValue(typeEnumVal, enumValueIndex) 642 if err != nil { 643 return err 644 } 645 646 if err = rs.Append(index, false); err != nil { 647 return err 648 } 649 } 650 } 651 return nil 652 } 653 654 // CastNanoToTimestamp returns timestamp string according to the nano 655 func CastNanoToTimestamp(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int) error { 656 rs := vector.MustFunctionResult[types.Varlena](result) 657 nanos := vector.GenerateFunctionFixedTypeParameter[int64](ivecs[0]) 658 659 layout := "2006-01-02 15:04:05.999999999" 660 for i := uint64(0); i < uint64(length); i++ { 661 nano, null := nanos.GetValue(i) 662 if null { 663 if err := rs.AppendBytes(nil, true); err != nil { 664 return err 665 } 666 } else { 667 t := time.Unix(0, nano).UTC() 668 if err := rs.AppendBytes([]byte(t.Format(layout)), false); err != nil { 669 return err 670 } 671 } 672 } 673 return nil 674 }