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  }