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  }