github.com/matrixorigin/matrixone@v1.2.0/pkg/util/export/table/table.go (about) 1 // Copyright 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 table 16 17 import ( 18 "context" 19 "encoding/hex" 20 "fmt" 21 "math" 22 "slices" 23 "strconv" 24 "strings" 25 "sync" 26 "time" 27 28 "github.com/matrixorigin/matrixone/pkg/common/moerr" 29 "github.com/matrixorigin/matrixone/pkg/common/util" 30 "github.com/matrixorigin/matrixone/pkg/container/types" 31 "github.com/matrixorigin/matrixone/pkg/logutil" 32 "github.com/matrixorigin/matrixone/pkg/util/batchpipe" 33 ) 34 35 const ExternalFilePath = "__mo_filepath" 36 37 type CsvOptions struct { 38 FieldTerminator rune // like: ',' 39 EncloseRune rune // like: '"' 40 Terminator rune // like: '\n' 41 } 42 43 var CommonCsvOptions = &CsvOptions{ 44 FieldTerminator: ',', 45 EncloseRune: '"', 46 Terminator: '\n', 47 } 48 49 type ColType int 50 51 const ( 52 TSkip ColType = iota 53 TDatetime 54 TUint32 55 TInt32 56 TUint64 57 TInt64 58 TFloat64 59 TJson 60 TText 61 TVarchar 62 TChar 63 TBool 64 TBytes // only used in ColumnField 65 TUuid // only used in ColumnField 66 TFloat32 67 TTimestamp 68 TBit 69 TBlob 70 TEnum 71 ) 72 73 func (c *ColType) ToType() types.Type { 74 switch *c { 75 case TDatetime: 76 typ := types.T_datetime.ToType() 77 typ.Scale = 6 78 return typ 79 case TTimestamp: 80 return types.T_timestamp.ToType() 81 case TUint32: 82 return types.T_uint32.ToType() 83 case TInt32: 84 return types.T_int32.ToType() 85 case TUint64: 86 return types.T_uint64.ToType() 87 case TInt64: 88 return types.T_int64.ToType() 89 case TFloat32: 90 return types.T_float32.ToType() 91 case TFloat64: 92 return types.T_float64.ToType() 93 case TJson: 94 return types.T_json.ToType() 95 case TText: 96 return types.T_text.ToType() 97 case TBool: 98 return types.T_bool.ToType() 99 case TVarchar: 100 return types.T_varchar.ToType() 101 //TODO : Need to see how T_array should be included in this class. 102 case TChar: 103 return types.T_char.ToType() 104 //TODO : Need to see how T_array should be included in this class. 105 case TSkip: 106 fallthrough 107 case TBit: 108 return types.T_bit.ToType() 109 case TBlob: 110 return types.T_blob.ToType() 111 case TEnum: 112 return types.T_enum.ToType() 113 default: 114 panic("not support ColType") 115 } 116 } 117 118 func (c *ColType) String(scale int) string { 119 switch *c { 120 case TDatetime: 121 return "Datetime(6)" 122 case TTimestamp: 123 return "TIMESTAMP" 124 case TUint32: 125 return "INT UNSIGNED" 126 case TInt32: 127 return "INT" 128 case TUint64: 129 return "BIGINT UNSIGNED" 130 case TInt64: 131 return "BIGINT" 132 case TFloat64: 133 return "DOUBLE" 134 case TJson: 135 return "JSON" 136 case TText: 137 return "TEXT" 138 case TBool: 139 return "BOOL" 140 case TVarchar: 141 if scale == 0 { 142 scale = 1024 143 } 144 return fmt.Sprintf("VARCHAR(%d)", scale) 145 case TChar: 146 if scale == 0 { 147 scale = 1024 148 } 149 return fmt.Sprintf("CHAR(%d)", scale) 150 case TBlob: 151 return "BLOB" 152 case TEnum: 153 return "ENUM" 154 case TSkip: 155 panic("not support SkipType") 156 default: 157 panic(fmt.Sprintf("not support ColType: %v", c)) 158 } 159 } 160 161 func StringColumn(name, comment string) Column { 162 return Column{ 163 Name: name, 164 ColType: TVarchar, 165 Scale: 1024, 166 Default: "", 167 Comment: comment, 168 } 169 } 170 func StringDefaultColumn(name, defaultVal, comment string) Column { 171 return Column{ 172 Name: name, 173 ColType: TVarchar, 174 Scale: 1024, 175 Default: defaultVal, 176 Comment: comment, 177 } 178 } 179 func StringWithScale(name string, scale int, comment string) Column { 180 return Column{ 181 Name: name, 182 ColType: TVarchar, 183 Scale: scale, 184 Default: "", 185 Comment: comment, 186 } 187 } 188 189 func UuidStringColumn(name, comment string) Column { 190 col := StringColumn(name, comment) 191 col.Scale = 36 192 return col 193 } 194 195 func SpanIDStringColumn(name, comment string) Column { 196 col := StringDefaultColumn(name, "0", comment) 197 col.Scale = 16 198 return col 199 } 200 201 func TextColumn(name, comment string) Column { 202 return Column{ 203 Name: name, 204 ColType: TText, 205 Default: "", 206 Comment: comment, 207 } 208 } 209 210 func TextDefaultColumn(name, defaultVal, comment string) Column { 211 return Column{ 212 Name: name, 213 ColType: TText, 214 Default: defaultVal, 215 Comment: comment, 216 } 217 } 218 219 func DatetimeColumn(name, comment string) Column { 220 return Column{ 221 Name: name, 222 ColType: TDatetime, 223 Default: "", 224 Comment: comment, 225 } 226 } 227 228 func JsonColumn(name, comment string) Column { 229 return Column{ 230 Name: name, 231 ColType: TJson, 232 Default: "{}", 233 Comment: comment, 234 } 235 } 236 237 func ValueColumn(name, comment string) Column { 238 return Column{ 239 Name: name, 240 ColType: TFloat64, 241 Default: "0.0", 242 Comment: comment, 243 } 244 } 245 246 func Int64Column(name, comment string) Column { 247 return Column{ 248 Name: name, 249 ColType: TInt64, 250 Default: "0", 251 Comment: comment, 252 } 253 } 254 255 func UInt64Column(name, comment string) Column { 256 return Column{ 257 Name: name, 258 ColType: TUint64, 259 Default: "0", 260 Comment: comment, 261 } 262 } 263 264 func Int32Column(name, comment string) Column { 265 return Column{ 266 Name: name, 267 ColType: TInt32, 268 Default: "0", 269 Comment: comment, 270 } 271 } 272 273 func UInt32Column(name, comment string) Column { 274 return Column{ 275 Name: name, 276 ColType: TUint32, 277 Default: "0", 278 Comment: comment, 279 } 280 } 281 282 func BoolColumn(name, comment string) Column { 283 return Column{ 284 Name: name, 285 ColType: TBool, 286 Default: "false", 287 Comment: comment, 288 } 289 } 290 291 func TimestampDefaultColumn(name, defaultVal, comment string) Column { 292 return Column{ 293 Name: name, 294 ColType: TTimestamp, 295 Default: defaultVal, 296 Comment: comment, 297 } 298 } 299 300 type Column struct { 301 Name string 302 ColType ColType 303 // Scale default 0, usually for varchar 304 Scale int 305 Default string 306 Comment string 307 Alias string // only use in view 308 } 309 310 // ToCreateSql return column scheme in create sql 311 // - case 1: `column_name` varchar(36) DEFAULT "def_val" COMMENT "what am I, with default." 312 // - case 2: `column_name` varchar(36) NOT NULL COMMENT "what am I. Without default, SO NOT NULL." 313 func (col *Column) ToCreateSql(ctx context.Context) string { 314 sb := strings.Builder{} 315 sb.WriteString(fmt.Sprintf("`%s` %s ", col.Name, col.ColType.String(col.Scale))) 316 if col.ColType == TJson { 317 sb.WriteString("NOT NULL ") 318 if len(col.Default) == 0 { 319 panic(moerr.NewNotSupported(ctx, "json column need default in csv, but not in schema")) 320 } 321 } else if len(col.Default) > 0 { 322 sb.WriteString(fmt.Sprintf("DEFAULT %q ", col.Default)) 323 } else { 324 sb.WriteString("NOT NULL ") 325 } 326 sb.WriteString(fmt.Sprintf("COMMENT %q", col.Comment)) 327 return sb.String() 328 } 329 330 var _ batchpipe.HasName = (*Table)(nil) 331 332 var NormalTableEngine = "TABLE" 333 334 // ExternalTableEngine 335 // Deprecated 336 var ExternalTableEngine = "EXTERNAL" 337 338 type SchemaDiff struct { 339 AddedColumns []Column 340 TableName string 341 DatabaseName string 342 } 343 344 type Table struct { 345 Account string 346 Database string 347 Table string 348 Columns []Column 349 PrimaryKeyColumn []Column 350 ClusterBy []Column 351 Engine string 352 Comment string 353 // PathBuilder help to desc param 'infile' 354 PathBuilder PathBuilder 355 // AccountColumn help to split data in account's filepath 356 AccountColumn *Column 357 // TimestampColumn help to purge data 358 TimestampColumn *Column 359 // TableOptions default is nil, see GetTableOptions 360 TableOptions TableOptions 361 // SupportUserAccess default false. if true, user account can access. 362 SupportUserAccess bool 363 // SupportConstAccess default false. if true, use Table.Account first 364 SupportConstAccess bool 365 366 // name2ColumnIdx used in Row 367 name2ColumnIdx map[string]int 368 // accessIdx used in Row 369 accountIdx int 370 371 // The original create table sql of the system table. If the system table is created by ddl, 372 // If the system table was created by DDL, the original creation sql will be used when upgrading the new table 373 // Note: ToCreateSql() converts a table object as a table creation statement based on its known basic properties 374 CreateTableSql string 375 376 // The original create view sql of the system view 377 CreateViewSql string 378 } 379 380 func (tbl *Table) Clone() *Table { 381 t := &Table{} 382 *t = *tbl 383 return t 384 } 385 386 func (tbl *Table) GetName() string { 387 return tbl.Table 388 } 389 func (tbl *Table) GetDatabase() string { 390 return tbl.Database 391 } 392 393 // GetIdentify return identify like database.table 394 func (tbl *Table) GetIdentify() string { 395 return fmt.Sprintf("%s.%s", tbl.Database, tbl.Table) 396 } 397 398 type TableOptions interface { 399 FormatDdl(ddl string) string 400 // GetCreateOptions return option for `create {option}table`, which should end with ' ' 401 GetCreateOptions() string 402 GetTableOptions(PathBuilder) string 403 } 404 405 func (tbl *Table) ToCreateSql(ctx context.Context, ifNotExists bool) string { 406 407 TableOptions := tbl.GetTableOptions(ctx) 408 409 const newLineCharacter = ",\n" 410 sb := strings.Builder{} 411 // create table 412 sb.WriteString("CREATE ") 413 switch strings.ToUpper(tbl.Engine) { 414 case ExternalTableEngine: 415 sb.WriteString(TableOptions.GetCreateOptions()) 416 case NormalTableEngine: 417 sb.WriteString(TableOptions.GetCreateOptions()) 418 default: 419 panic(moerr.NewInternalError(ctx, "NOT support engine: %s", tbl.Engine)) 420 } 421 sb.WriteString("TABLE ") 422 if ifNotExists { 423 sb.WriteString("IF NOT EXISTS ") 424 } 425 // table name 426 sb.WriteString(fmt.Sprintf("`%s`.`%s`(", tbl.Database, tbl.Table)) 427 // columns 428 for idx, col := range tbl.Columns { 429 if idx > 0 { 430 sb.WriteString(newLineCharacter) 431 } else { 432 sb.WriteRune('\n') 433 } 434 sb.WriteString(col.ToCreateSql(ctx)) 435 } 436 // primary key 437 if len(tbl.PrimaryKeyColumn) > 0 && tbl.Engine != ExternalTableEngine { 438 sb.WriteString(newLineCharacter) 439 sb.WriteString("PRIMARY KEY (") 440 for idx, col := range tbl.PrimaryKeyColumn { 441 if idx > 0 { 442 sb.WriteString(`, `) 443 } 444 sb.WriteString(fmt.Sprintf("`%s`", col.Name)) 445 } 446 sb.WriteString(`)`) 447 } 448 sb.WriteString("\n)") 449 450 if len(tbl.Comment) > 0 && slices.Contains([]string{"statement_info", "rawlog"}, tbl.Table) { 451 sb.WriteString(fmt.Sprintf(" COMMENT %q ", tbl.Comment)) 452 } 453 // cluster by 454 if len(tbl.ClusterBy) > 0 && tbl.Engine != ExternalTableEngine { 455 sb.WriteString(" cluster by (") 456 for idx, col := range tbl.ClusterBy { 457 if idx > 0 { 458 sb.WriteString(`, `) 459 } 460 sb.WriteString(fmt.Sprintf("`%s`", col.Name)) 461 } 462 sb.WriteString(`)`) 463 } 464 sb.WriteString(TableOptions.GetTableOptions(tbl.PathBuilder)) 465 466 return sb.String() 467 } 468 469 func (tbl *Table) GetTableOptions(ctx context.Context) TableOptions { 470 if tbl.TableOptions != nil { 471 return tbl.TableOptions 472 } 473 return GetOptionFactory(ctx, tbl.Engine)(tbl.Database, tbl.Table, tbl.Account) 474 } 475 476 type ViewOption func(view *View) 477 478 func (opt ViewOption) Apply(view *View) { 479 opt(view) 480 } 481 482 type WhereCondition interface { 483 String() string 484 } 485 486 type CreateSql interface { 487 String(ctx context.Context, ifNotExists bool) string 488 } 489 490 type View struct { 491 Database string 492 Table string 493 OriginTable *Table 494 Columns []Column 495 // Condition will be used in View.ToCreateSql 496 Condition WhereCondition 497 // CreateSql will be used in View.ToCreateSql 498 CreateSql CreateSql 499 // SupportUserAccess default false. if true, user account can access. 500 SupportUserAccess bool 501 } 502 503 func WithColumn(c Column) ViewOption { 504 return ViewOption(func(v *View) { 505 v.Columns = append(v.Columns, c) 506 }) 507 } 508 509 func SupportUserAccess(support bool) ViewOption { 510 return ViewOption(func(v *View) { 511 v.SupportUserAccess = support 512 }) 513 } 514 515 // ToCreateSql return create view sql. 516 // If tbl.CreateSql is not nil, return tbl.CreateSql.String(), 517 // Else return 518 func (tbl *View) ToCreateSql(ctx context.Context, ifNotExists bool) string { 519 if tbl.CreateSql != nil { 520 return tbl.CreateSql.String(ctx, ifNotExists) 521 } else { 522 return tbl.generateCreateSql(ctx, ifNotExists) 523 } 524 } 525 526 // generateCreateSql generate create view sql. 527 func (tbl *View) generateCreateSql(ctx context.Context, ifNotExists bool) string { 528 sb := strings.Builder{} 529 // create table 530 sb.WriteString("CREATE VIEW ") 531 if ifNotExists { 532 sb.WriteString("IF NOT EXISTS ") 533 } 534 // table name 535 sb.WriteString(fmt.Sprintf("`%s`.`%s` as ", tbl.Database, tbl.Table)) 536 sb.WriteString("select ") 537 // columns 538 for idx, col := range tbl.Columns { 539 if idx > 0 { 540 sb.WriteString(", ") 541 } 542 sb.WriteString(fmt.Sprintf("`%s`", col.Name)) 543 if len(col.Alias) > 0 { 544 sb.WriteString(fmt.Sprintf(" as `%s`", col.Alias)) 545 } 546 } 547 if tbl.OriginTable.Engine == ExternalTableEngine { 548 sb.WriteString(fmt.Sprintf(", mo_log_date(`%s`) as `log_date`", ExternalFilePath)) 549 sb.WriteString(fmt.Sprintf(", `%s`", ExternalFilePath)) 550 } 551 sb.WriteString(fmt.Sprintf(" from `%s`.`%s` where ", tbl.OriginTable.Database, tbl.OriginTable.Table)) 552 sb.WriteString(tbl.Condition.String()) 553 554 return sb.String() 555 } 556 557 type ViewSingleCondition struct { 558 Column Column 559 Table string 560 } 561 562 func (tbl *ViewSingleCondition) String() string { 563 return fmt.Sprintf("`%s` = %q", tbl.Column.Name, tbl.Table) 564 } 565 566 type ViewCreateSqlString string 567 568 func (s ViewCreateSqlString) String(ctx context.Context, ifNotExists bool) string { 569 return string(s) 570 } 571 572 type ColumnField struct { 573 Type ColType 574 Integer int64 575 String string 576 Bytes []byte 577 Interface interface{} 578 } 579 580 // GetFloat64 return float64 581 // which store in Integer 582 func (cf *ColumnField) GetFloat64() float64 { 583 return math.Float64frombits(uint64(cf.Integer)) 584 } 585 586 func (cf *ColumnField) GetTime() time.Time { 587 if cf.Interface != nil { 588 return time.Unix(0, cf.Integer).In(cf.Interface.(*time.Location)) 589 } else { 590 if cf.Integer == 0 { 591 return time.Time{} 592 } else { 593 return time.Unix(0, cf.Integer) 594 } 595 } 596 } 597 598 func (cf *ColumnField) EncodeBytes() string { 599 return hex.EncodeToString(cf.Bytes) 600 } 601 602 func (cf *ColumnField) EncodeUuid() (dst [36]byte) { 603 util.EncodeUUIDHex(dst[:], cf.Bytes) 604 return 605 } 606 607 func (cf *ColumnField) EncodedDatetime(dst []byte) []byte { 608 return Time2DatetimeBuffed(cf.GetTime(), dst[:0]) 609 } 610 611 var emptyTime = time.Time{} 612 613 func TimeField(val time.Time) ColumnField { 614 if val == emptyTime { 615 return ColumnField{Type: TDatetime, Integer: 0, Interface: nil} 616 } 617 secs := val.UnixNano() 618 return ColumnField{Type: TDatetime, Integer: secs, Interface: val.Location()} 619 } 620 621 func Uint64Field(val uint64) ColumnField { 622 return ColumnField{Type: TUint64, Integer: int64(val)} 623 } 624 625 func Int64Field(val int64) ColumnField { 626 return ColumnField{Type: TInt64, Integer: val} 627 } 628 629 func Float64Field(val float64) ColumnField { 630 return ColumnField{Type: TFloat64, Integer: int64(math.Float64bits(val))} 631 } 632 633 // JsonField will have same effect as StringField 634 func JsonField(val string) ColumnField { 635 return ColumnField{Type: TJson, String: val} 636 } 637 638 func StringField(val string) ColumnField { 639 return ColumnField{Type: TVarchar, String: val} 640 } 641 642 func BytesField(val []byte) ColumnField { 643 return ColumnField{Type: TBytes, Bytes: val} 644 } 645 646 func UuidField(val []byte) ColumnField { 647 return ColumnField{Type: TUuid, Bytes: val} 648 } 649 650 type Row struct { 651 Table *Table 652 653 Columns []ColumnField 654 CsvColumns []string 655 } 656 657 func (tbl *Table) GetRow(ctx context.Context) *Row { 658 row := NewRow() 659 row.Table = tbl 660 row.Columns = make([]ColumnField, len(tbl.Columns)) 661 662 if len(tbl.name2ColumnIdx) == 0 { 663 tbl.name2ColumnIdx = make(map[string]int, len(tbl.Columns)) 664 for idx, col := range tbl.Columns { 665 if _, exist := tbl.name2ColumnIdx[col.Name]; exist { 666 panic(moerr.NewInternalError(ctx, "%s table has duplicate column name: %s", tbl.GetIdentify(), col.Name)) 667 } 668 tbl.name2ColumnIdx[col.Name] = idx 669 } 670 if tbl.AccountColumn != nil { 671 idx, exist := tbl.name2ColumnIdx[tbl.AccountColumn.Name] 672 if !exist { 673 panic(moerr.NewInternalError(ctx, "%s table missing %s column", tbl.GetName(), tbl.AccountColumn.Name)) 674 } 675 tbl.accountIdx = idx 676 } else { 677 tbl.accountIdx = -1 678 } 679 } 680 return row 681 } 682 683 func NewRow() *Row { 684 return gRowPool.Get().(*Row) 685 } 686 687 var gRowPool = sync.Pool{New: func() any { 688 return &Row{ 689 Table: nil, 690 Columns: nil, 691 CsvColumns: nil, 692 } 693 }} 694 695 func (r *Row) Free() { 696 r.clean() 697 gRowPool.Put(r) 698 } 699 700 func (r *Row) clean() { 701 r.Table = nil 702 r.Columns = nil 703 r.CsvColumns = nil 704 } 705 706 func (r *Row) Clone() *Row { 707 n := NewRow() 708 n.Table = r.Table 709 n.Columns = r.Columns 710 n.CsvColumns = r.CsvColumns 711 return n 712 } 713 714 func (r *Row) Reset() { 715 for idx, typ := range r.Table.Columns { 716 switch typ.ColType.ToType().Oid { 717 case types.T_bit: 718 r.Columns[idx] = Uint64Field(0) 719 case types.T_int64: 720 r.Columns[idx] = Int64Field(0) 721 case types.T_uint64: 722 r.Columns[idx] = Uint64Field(0) 723 case types.T_float64: 724 r.Columns[idx] = Float64Field(0) 725 case types.T_char, types.T_varchar, 726 types.T_binary, types.T_varbinary, types.T_blob, types.T_text: 727 r.Columns[idx] = StringField(typ.Default) 728 case types.T_json: 729 r.Columns[idx] = StringField(typ.Default) 730 case types.T_datetime: 731 r.Columns[idx] = TimeField(ZeroTime) 732 default: 733 logutil.Errorf("the value type %v is not SUPPORT", typ.ColType.ToType().String()) 734 panic("the value type is not support now") 735 } 736 } 737 } 738 739 // GetAccount 740 // return r.Table.Account if r.Table.SupportConstAccess 741 // else return r.Columns[r.AccountIdx] if r.AccountIdx >= 0 and r.Table.PathBuilder.SupportAccountStrategy, 742 // else return "sys" 743 func (r *Row) GetAccount() string { 744 if r.Table.SupportConstAccess && len(r.Table.Account) > 0 { 745 return r.Table.Account 746 } 747 if r.Table.PathBuilder.SupportAccountStrategy() && r.Table.accountIdx >= 0 { 748 return r.Columns[r.Table.accountIdx].String 749 } 750 return AccountSys 751 } 752 753 func (r *Row) SetVal(col string, cf ColumnField) { 754 if idx, exist := r.Table.name2ColumnIdx[col]; !exist { 755 logutil.Fatalf("column(%s) not exist in table(%s)", col, r.Table.Table) 756 } else { 757 r.Columns[idx] = cf 758 } 759 } 760 761 func (r *Row) SetColumnVal(col Column, cf ColumnField) { 762 if col.ColType == TVarchar && len(cf.String) > col.Scale { 763 cf.String = cf.String[0:col.Scale] 764 } 765 r.SetVal(col.Name, cf) 766 } 767 768 // ToStrings output all column as string 769 func (r *Row) ToStrings() []string { 770 col := make([]string, len(r.Table.Columns)) 771 for idx, typ := range r.Table.Columns { 772 switch typ.ColType.ToType().Oid { 773 case types.T_bit: 774 col[idx] = fmt.Sprintf("%d", uint64(r.Columns[idx].Integer)) 775 case types.T_int64: 776 col[idx] = fmt.Sprintf("%d", r.Columns[idx].Integer) 777 case types.T_uint64: 778 col[idx] = fmt.Sprintf("%d", uint64(r.Columns[idx].Integer)) 779 case types.T_float64: 780 col[idx] = strconv.FormatFloat(r.Columns[idx].GetFloat64(), 'f', -1, 64) 781 case types.T_char, types.T_varchar, 782 types.T_binary, types.T_varbinary, types.T_blob, types.T_text: 783 switch r.Columns[idx].Type { 784 case TBytes: 785 // hack way for json column, avoid early copy. pls see more in BytesTIPs 786 val := r.Columns[idx].Bytes 787 if len(val) == 0 { 788 col[idx] = typ.Default 789 } else { 790 col[idx] = string(r.Columns[idx].Bytes) 791 } 792 case TUuid: 793 dst := r.Columns[idx].EncodeUuid() 794 col[idx] = string(dst[:]) 795 default: 796 val := r.Columns[idx].String 797 if len(val) == 0 { 798 val = typ.Default 799 } 800 col[idx] = val 801 } 802 case types.T_json: 803 switch r.Columns[idx].Type { 804 case TJson, TVarchar, TText: 805 val := r.Columns[idx].String 806 if len(val) == 0 { 807 val = typ.Default 808 } 809 col[idx] = val 810 case TBytes: 811 // BytesTIPs: hack way for json column, avoid early copy. 812 // Data-safety depends on Writer call Row.ToStrings() before IBuffer2SqlItem.Free() 813 // important: 814 // StatementInfo's execPlanCol / statsCol, this two column will be free by StatementInfo.Free() 815 val := r.Columns[idx].Bytes 816 if len(val) == 0 { 817 col[idx] = typ.Default 818 } else { 819 col[idx] = string(val) 820 } 821 } 822 case types.T_datetime: 823 col[idx] = Time2DatetimeString(r.Columns[idx].GetTime()) 824 default: 825 logutil.Errorf("the value type %v is not SUPPORT", typ.ColType.ToType().String()) 826 panic("the value type is not support now") 827 } 828 } 829 return col 830 } 831 832 func (r *Row) GetRawColumns() []ColumnField { 833 return r.Columns 834 } 835 836 // GetCsvStrings not format 837 func (r *Row) GetCsvStrings() []string { 838 return r.CsvColumns 839 } 840 841 func (r *Row) ParseRow(cols []string) error { 842 // fixme: check len(r.Name2ColumnIdx) != len(cols) 843 r.CsvColumns = cols 844 return nil 845 } 846 847 // CsvPrimaryKey return string = concat($CsvCol[PrimaryKeyColumnIdx], '-') 848 // Deprecated 849 func (r *Row) CsvPrimaryKey() string { 850 if len(r.Table.PrimaryKeyColumn) == 0 { 851 return "" 852 } 853 if len(r.Table.PrimaryKeyColumn) == 1 { 854 return r.CsvColumns[r.Table.name2ColumnIdx[r.Table.PrimaryKeyColumn[0].Name]] 855 } 856 sb := strings.Builder{} 857 for _, col := range r.Table.PrimaryKeyColumn { 858 sb.WriteString(r.CsvColumns[r.Table.name2ColumnIdx[col.Name]]) 859 sb.WriteRune('-') 860 } 861 return sb.String() 862 } 863 864 func (r *Row) Size() (size int64) { 865 if len(r.CsvColumns) > 0 { 866 for _, v := range r.CsvColumns { 867 size += int64(len(v)) 868 } 869 return size 870 } 871 for idx, typ := range r.Table.Columns { 872 switch typ.ColType.ToType().Oid { 873 case types.T_bit: 874 size += 8 875 case types.T_int64: 876 size += 8 877 case types.T_uint64: 878 size += 8 879 case types.T_float64: 880 size += 8 881 case types.T_char, types.T_varchar, 882 types.T_binary, types.T_varbinary, types.T_blob, types.T_text: 883 size += int64(len(r.Columns[idx].String)) 884 case types.T_json: 885 size += int64(len(r.Columns[idx].String)) 886 case types.T_datetime: 887 size += 16 888 } 889 } 890 return 891 } 892 893 var _ TableOptions = (*NoopTableOptions)(nil) 894 895 type NoopTableOptions struct{} 896 897 func (o NoopTableOptions) FormatDdl(ddl string) string { return ddl } 898 func (o NoopTableOptions) GetCreateOptions() string { return "" } 899 func (o NoopTableOptions) GetTableOptions(PathBuilder) string { return "" } 900 901 var _ TableOptions = (*CsvTableOptions)(nil) 902 903 type CsvTableOptions struct { 904 Formatter string 905 DbName string 906 TblName string 907 Account string 908 } 909 910 func getExternalTableDDLPrefix(sql string) string { 911 return strings.Replace(sql, "CREATE TABLE", "CREATE EXTERNAL TABLE", 1) 912 } 913 914 func (o *CsvTableOptions) FormatDdl(ddl string) string { 915 return getExternalTableDDLPrefix(ddl) 916 } 917 918 func (o *CsvTableOptions) GetCreateOptions() string { 919 return "EXTERNAL " 920 } 921 922 func (o *CsvTableOptions) GetTableOptions(builder PathBuilder) string { 923 if builder == nil { 924 builder = NewDBTablePathBuilder() 925 } 926 if len(o.Formatter) > 0 { 927 return fmt.Sprintf(o.Formatter, builder.BuildETLPath(o.DbName, o.TblName, o.Account)) 928 } 929 return "" 930 } 931 932 func GetOptionFactory(ctx context.Context, engine string) func(db string, tbl string, account string) TableOptions { 933 var infileFormatter = ` infile{"filepath"="etl:%s","compression"="none"}` + 934 ` FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 0 lines` 935 switch engine { 936 case NormalTableEngine: 937 return func(_, _, _ string) TableOptions { return NoopTableOptions{} } 938 case ExternalTableEngine: 939 return func(db, tbl, account string) TableOptions { 940 return &CsvTableOptions{Formatter: infileFormatter, DbName: db, TblName: tbl, Account: account} 941 } 942 default: 943 panic(moerr.NewInternalError(ctx, "unknown engine: %s", engine)) 944 } 945 } 946 947 var gTable map[string]*Table 948 var mux sync.Mutex 949 950 // RegisterTableDefine return old one, if already registered 951 func RegisterTableDefine(table *Table) *Table { 952 mux.Lock() 953 defer mux.Unlock() 954 if len(gTable) == 0 { 955 gTable = make(map[string]*Table) 956 } 957 id := table.GetIdentify() 958 old := gTable[id] 959 gTable[id] = table 960 return old 961 } 962 963 // GetAllTables holds all tables' Definition which should be handled in ETLMerge 964 func GetAllTables() []*Table { 965 mux.Lock() 966 defer mux.Unlock() 967 tables := make([]*Table, 0, len(gTable)) 968 for _, tbl := range gTable { 969 tables = append(tables, tbl) 970 } 971 return tables 972 } 973 974 // SetPathBuilder 975 // 976 // Deprecated. Please init static 977 func SetPathBuilder(ctx context.Context, pathBuilder string) error { 978 tables := GetAllTables() 979 bp := PathBuilderFactory(pathBuilder) 980 if bp == nil { 981 return moerr.NewNotSupported(ctx, "not support PathBuilder: %s", pathBuilder) 982 } 983 for _, tbl := range tables { 984 tbl.PathBuilder = bp 985 } 986 return nil 987 } 988 989 var ZeroTime = time.Time{} 990 991 const timestampFormatter = "2006-01-02 15:04:05.000000" 992 993 func Time2DatetimeString(t time.Time) string { 994 return t.Format(timestampFormatter) 995 } 996 997 // Time2DatetimeBuffed output datetime string to buffer 998 // len(buf) should >= max(64, len(timestampFormatter) + 10) 999 func Time2DatetimeBuffed(t time.Time, buf []byte) []byte { 1000 return t.AppendFormat(buf, timestampFormatter) 1001 }