github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/build_constraint_util.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 plan
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	"github.com/google/uuid"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/catalog"
    24  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    25  	"github.com/matrixorigin/matrixone/pkg/config"
    26  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    27  	"github.com/matrixorigin/matrixone/pkg/container/types"
    28  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    29  	"github.com/matrixorigin/matrixone/pkg/defines"
    30  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    31  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    32  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    33  	"github.com/matrixorigin/matrixone/pkg/sql/plan/function"
    34  	"github.com/matrixorigin/matrixone/pkg/sql/util"
    35  )
    36  
    37  const (
    38  	derivedTableName                   = "_t"
    39  	defaultmaxRowThenUnusePkFilterExpr = 1024
    40  )
    41  
    42  type dmlSelectInfo struct {
    43  	typ string
    44  
    45  	projectList    []*Expr
    46  	tblInfo        *dmlTableInfo
    47  	idx            int32
    48  	rootId         int32
    49  	derivedTableId int32
    50  
    51  	onDuplicateIdx      []int32
    52  	onDuplicateExpr     map[string]*Expr
    53  	onDuplicateNeedAgg  bool // if table have pk & unique key, that will be true.
    54  	onDuplicateIsIgnore bool
    55  }
    56  
    57  type dmlTableInfo struct {
    58  	typ            string
    59  	objRef         []*ObjectRef
    60  	tableDefs      []*TableDef
    61  	isClusterTable []bool
    62  	haveConstraint bool
    63  	isMulti        bool
    64  	needAggFilter  bool
    65  	updateKeys     []map[string]tree.Expr // This slice index correspond to tableDefs
    66  	oldColPosMap   []map[string]int       // origin table values to their position in derived table
    67  	newColPosMap   []map[string]int       // insert/update values to their position in derived table
    68  	nameToIdx      map[string]int         // Mapping of table full path name to tableDefs index,such as: 'tpch.nation -> 0'
    69  	idToName       map[uint64]string      // Mapping of tableId to full path name of table
    70  	alias          map[string]int         // Mapping of table aliases to tableDefs array index,If there is no alias, replace it with the original name of the table
    71  }
    72  
    73  var constTextType plan.Type
    74  
    75  func init() {
    76  	typ := types.T_text.ToType()
    77  	constTextType = makePlan2Type(&typ)
    78  }
    79  
    80  func getAliasToName(ctx CompilerContext, expr tree.TableExpr, alias string, aliasMap map[string][2]string) {
    81  	switch t := expr.(type) {
    82  	case *tree.TableName:
    83  		dbName := string(t.SchemaName)
    84  		if dbName == "" {
    85  			dbName = ctx.DefaultDatabase()
    86  		}
    87  		tblName := string(t.ObjectName)
    88  		if alias != "" {
    89  			aliasMap[alias] = [2]string{dbName, tblName}
    90  		}
    91  	case *tree.AliasedTableExpr:
    92  		alias := string(t.As.Alias)
    93  		getAliasToName(ctx, t.Expr, alias, aliasMap)
    94  	case *tree.JoinTableExpr:
    95  		getAliasToName(ctx, t.Left, alias, aliasMap)
    96  		getAliasToName(ctx, t.Right, alias, aliasMap)
    97  	}
    98  }
    99  
   100  func getUpdateTableInfo(ctx CompilerContext, stmt *tree.Update) (*dmlTableInfo, error) {
   101  	tblInfo, err := getDmlTableInfo(ctx, stmt.Tables, stmt.With, nil, "update")
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	// check update field and set updateKeys
   107  	usedTbl := make(map[string]map[string]tree.Expr)
   108  	allColumns := make(map[string]map[string]struct{})
   109  	for alias, idx := range tblInfo.alias {
   110  		allColumns[alias] = make(map[string]struct{})
   111  		for _, col := range tblInfo.tableDefs[idx].Cols {
   112  			allColumns[alias][col.Name] = struct{}{}
   113  		}
   114  	}
   115  
   116  	appendToTbl := func(table, column string, expr tree.Expr) {
   117  		if _, exists := usedTbl[table]; !exists {
   118  			usedTbl[table] = make(map[string]tree.Expr)
   119  		}
   120  		usedTbl[table][column] = expr
   121  	}
   122  
   123  	for _, updateExpr := range stmt.Exprs {
   124  		if len(updateExpr.Names) > 1 {
   125  			return nil, moerr.NewNYI(ctx.GetContext(), "unsupport expr")
   126  		}
   127  		parts := updateExpr.Names[0]
   128  		expr := updateExpr.Expr
   129  		if parts.NumParts > 1 {
   130  			colName := parts.Parts[0]
   131  			tblName := parts.Parts[1]
   132  			if _, tblExists := tblInfo.alias[tblName]; tblExists {
   133  				if _, colExists := allColumns[tblName][colName]; colExists {
   134  					appendToTbl(tblName, colName, expr)
   135  				} else {
   136  					return nil, moerr.NewInternalError(ctx.GetContext(), "column '%v' not found in table %s", colName, tblName)
   137  				}
   138  			} else {
   139  				return nil, moerr.NewNoSuchTable(ctx.GetContext(), "", tblName)
   140  			}
   141  		} else {
   142  			colName := parts.Parts[0]
   143  			tblName := ""
   144  			found := false
   145  			for alias, colulmns := range allColumns {
   146  				if _, colExists := colulmns[colName]; colExists {
   147  					if tblName != "" {
   148  						return nil, moerr.NewInternalError(ctx.GetContext(), "Column '%v' in field list is ambiguous", colName)
   149  					}
   150  					found = true
   151  					appendToTbl(alias, colName, expr)
   152  				}
   153  			}
   154  			if !found && stmt.With != nil {
   155  				var str string
   156  				for i, c := range stmt.With.CTEs {
   157  					if i > 0 {
   158  						str += ", "
   159  					}
   160  					str += string(c.Name.Alias)
   161  				}
   162  				return nil, moerr.NewInternalError(ctx.GetContext(), "column '%v' not found in table or the target table %s of the UPDATE is not updatable", colName, str)
   163  			} else if !found {
   164  				return nil, moerr.NewInternalError(ctx.GetContext(), "column '%v' not found in table %s", colName, tblName)
   165  			}
   166  		}
   167  	}
   168  
   169  	// remove unused table
   170  	newTblInfo := &dmlTableInfo{
   171  		nameToIdx:     make(map[string]int),
   172  		idToName:      make(map[uint64]string),
   173  		alias:         make(map[string]int),
   174  		isMulti:       tblInfo.isMulti,
   175  		needAggFilter: tblInfo.needAggFilter,
   176  	}
   177  	for alias, columns := range usedTbl {
   178  		idx := tblInfo.alias[alias]
   179  		tblDef := tblInfo.tableDefs[idx]
   180  		newTblInfo.objRef = append(newTblInfo.objRef, tblInfo.objRef[idx])
   181  		newTblInfo.tableDefs = append(newTblInfo.tableDefs, tblDef)
   182  		newTblInfo.isClusterTable = append(newTblInfo.isClusterTable, tblInfo.isClusterTable[idx])
   183  		newTblInfo.alias[alias] = len(newTblInfo.tableDefs) - 1
   184  		newTblInfo.updateKeys = append(newTblInfo.updateKeys, columns)
   185  
   186  		if !newTblInfo.haveConstraint {
   187  			if len(tblDef.RefChildTbls) > 0 {
   188  				newTblInfo.haveConstraint = true
   189  			} else if len(tblDef.Fkeys) > 0 {
   190  				newTblInfo.haveConstraint = true
   191  			} else {
   192  				for _, indexdef := range tblDef.Indexes {
   193  					if indexdef.Unique {
   194  						newTblInfo.haveConstraint = true
   195  						break
   196  					}
   197  				}
   198  			}
   199  		}
   200  	}
   201  	for idx, ref := range newTblInfo.objRef {
   202  		key := ref.SchemaName + "." + ref.ObjName
   203  		newTblInfo.idToName[newTblInfo.tableDefs[idx].TblId] = key
   204  		newTblInfo.nameToIdx[key] = idx
   205  	}
   206  
   207  	return newTblInfo, nil
   208  }
   209  
   210  func setTableExprToDmlTableInfo(ctx CompilerContext, tbl tree.TableExpr, tblInfo *dmlTableInfo, aliasMap map[string][2]string, withMap map[string]struct{}) error {
   211  	var tblName, dbName, alias string
   212  
   213  	if aliasTbl, ok := tbl.(*tree.AliasedTableExpr); ok {
   214  		alias = string(aliasTbl.As.Alias)
   215  		tbl = aliasTbl.Expr
   216  	}
   217  
   218  	if joinTbl, ok := tbl.(*tree.JoinTableExpr); ok {
   219  		tblInfo.needAggFilter = true
   220  		err := setTableExprToDmlTableInfo(ctx, joinTbl.Left, tblInfo, aliasMap, withMap)
   221  		if err != nil {
   222  			return err
   223  		}
   224  		if joinTbl.Right != nil {
   225  			return setTableExprToDmlTableInfo(ctx, joinTbl.Right, tblInfo, aliasMap, withMap)
   226  		}
   227  		return nil
   228  	}
   229  
   230  	if baseTbl, ok := tbl.(*tree.TableName); ok {
   231  		tblName = string(baseTbl.ObjectName)
   232  		dbName = string(baseTbl.SchemaName)
   233  	}
   234  
   235  	if _, exist := withMap[tblName]; exist {
   236  		return nil
   237  	}
   238  
   239  	if aliasNames, exist := aliasMap[tblName]; exist {
   240  		alias = tblName // work in delete statement
   241  		dbName = aliasNames[0]
   242  		tblName = aliasNames[1]
   243  	}
   244  
   245  	if tblName == "" {
   246  		return nil
   247  	}
   248  
   249  	if dbName == "" {
   250  		dbName = ctx.DefaultDatabase()
   251  	}
   252  
   253  	// snapshot to fix
   254  	obj, tableDef := ctx.Resolve(dbName, tblName, Snapshot{TS: &timestamp.Timestamp{}})
   255  	if tableDef == nil {
   256  		return moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName)
   257  	}
   258  
   259  	if tableDef.TableType == catalog.SystemSourceRel {
   260  		return moerr.NewInvalidInput(ctx.GetContext(), "cannot insert/update/delete from source")
   261  	} else if tableDef.TableType == catalog.SystemExternalRel {
   262  		return moerr.NewInvalidInput(ctx.GetContext(), "cannot insert/update/delete from external table")
   263  	} else if tableDef.TableType == catalog.SystemViewRel {
   264  		return moerr.NewInvalidInput(ctx.GetContext(), "cannot insert/update/delete from view")
   265  	} else if tableDef.TableType == catalog.SystemSequenceRel && ctx.GetContext().Value(defines.BgKey{}) == nil {
   266  		return moerr.NewInvalidInput(ctx.GetContext(), "Cannot insert/update/delete from sequence")
   267  	}
   268  
   269  	var newCols []*ColDef
   270  	for _, col := range tableDef.Cols {
   271  		if col.Hidden && tblInfo.typ == "insert" {
   272  			if col.Name == catalog.FakePrimaryKeyColName {
   273  				// fake pk is auto increment, need to fill.
   274  				// TODO(fagongzi): we need to use a separate tag to mark the columns
   275  				// for these behaviors, instead of using column names, which needs to
   276  				// be changed after 0.8
   277  				newCols = append(newCols, col)
   278  			}
   279  		} else {
   280  			newCols = append(newCols, col)
   281  		}
   282  	}
   283  	// note: the `rowId` column has been excluded from `TableDef` in the `insert` statement
   284  	tableDef.Cols = newCols
   285  
   286  	isClusterTable := util.TableIsClusterTable(tableDef.GetTableType())
   287  	accountId, err := ctx.GetAccountId()
   288  	if err != nil {
   289  		return err
   290  	}
   291  	if isClusterTable && accountId != catalog.System_Account {
   292  		return moerr.NewInternalError(ctx.GetContext(), "only the sys account can insert/update/delete the cluster table")
   293  	}
   294  
   295  	if util.TableIsClusterTable(tableDef.GetTableType()) && accountId != catalog.System_Account {
   296  		return moerr.NewInternalError(ctx.GetContext(), "only the sys account can insert/update/delete the cluster table %s", tableDef.GetName())
   297  	}
   298  	if obj.PubInfo != nil {
   299  		return moerr.NewInternalError(ctx.GetContext(), "cannot insert/update/delete from public table")
   300  	}
   301  
   302  	if !tblInfo.haveConstraint {
   303  		if len(tableDef.RefChildTbls) > 0 {
   304  			tblInfo.haveConstraint = true
   305  		} else if len(tableDef.Fkeys) > 0 {
   306  			tblInfo.haveConstraint = true
   307  		} else {
   308  			for _, indexdef := range tableDef.Indexes {
   309  				if indexdef.Unique {
   310  					tblInfo.haveConstraint = true
   311  					break
   312  				}
   313  			}
   314  		}
   315  	}
   316  
   317  	nowIdx := len(tblInfo.tableDefs)
   318  	tblInfo.isClusterTable = append(tblInfo.isClusterTable, isClusterTable)
   319  	tblInfo.objRef = append(tblInfo.objRef, &ObjectRef{
   320  		Obj:        int64(tableDef.TblId),
   321  		SchemaName: dbName,
   322  		ObjName:    tblName,
   323  	})
   324  	tblInfo.tableDefs = append(tblInfo.tableDefs, tableDef)
   325  	key := dbName + "." + tblName
   326  	tblInfo.nameToIdx[key] = nowIdx
   327  	tblInfo.idToName[tableDef.TblId] = key
   328  	if alias == "" {
   329  		alias = tblName
   330  	}
   331  	tblInfo.alias[alias] = nowIdx
   332  
   333  	return nil
   334  }
   335  
   336  func getDmlTableInfo(ctx CompilerContext, tableExprs tree.TableExprs, with *tree.With, aliasMap map[string][2]string, typ string) (*dmlTableInfo, error) {
   337  	tblInfo := &dmlTableInfo{
   338  		typ:       typ,
   339  		nameToIdx: make(map[string]int),
   340  		idToName:  make(map[uint64]string),
   341  		alias:     make(map[string]int),
   342  	}
   343  
   344  	cteMap := make(map[string]struct{})
   345  	if with != nil {
   346  		for _, cte := range with.CTEs {
   347  			cteMap[string(cte.Name.Alias)] = struct{}{}
   348  		}
   349  	}
   350  
   351  	for _, tbl := range tableExprs {
   352  		err := setTableExprToDmlTableInfo(ctx, tbl, tblInfo, aliasMap, cteMap)
   353  		if err != nil {
   354  			return nil, err
   355  		}
   356  	}
   357  	tblInfo.isMulti = len(tblInfo.objRef) > 1
   358  	tblInfo.needAggFilter = tblInfo.needAggFilter || tblInfo.isMulti
   359  
   360  	return tblInfo, nil
   361  }
   362  
   363  func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Insert, info *dmlSelectInfo) (bool, map[string]bool, error) {
   364  	var err error
   365  	var syntaxHasColumnNames bool
   366  	var insertColumns []string
   367  	tableDef := info.tblInfo.tableDefs[0]
   368  	tableObjRef := info.tblInfo.objRef[0]
   369  	colToIdx := make(map[string]int)
   370  	oldColPosMap := make(map[string]int)
   371  	tableDef.Name2ColIndex = make(map[string]int32)
   372  	for i, col := range tableDef.Cols {
   373  		colToIdx[col.Name] = i
   374  		oldColPosMap[col.Name] = i
   375  		tableDef.Name2ColIndex[col.Name] = int32(i)
   376  	}
   377  	info.tblInfo.oldColPosMap = append(info.tblInfo.oldColPosMap, oldColPosMap)
   378  	info.tblInfo.newColPosMap = append(info.tblInfo.newColPosMap, oldColPosMap)
   379  
   380  	ifExistAutoPkCol := false
   381  	insertWithoutUniqueKeyMap := make(map[string]bool)
   382  
   383  	if insertColumns, err = getInsertColsFromStmt(builder.GetContext(), stmt, tableDef); err != nil {
   384  		return false, nil, err
   385  	}
   386  	if stmt.Columns != nil {
   387  		syntaxHasColumnNames = true
   388  	}
   389  
   390  	var astSlt *tree.Select
   391  	switch slt := stmt.Rows.Select.(type) {
   392  	// rewrite 'insert into tbl values (1,1)' to 'insert into tbl select * from (values row(1,1))'
   393  	case *tree.ValuesClause:
   394  		isAllDefault := false
   395  		if slt.Rows[0] == nil {
   396  			isAllDefault = true
   397  		}
   398  		if isAllDefault {
   399  			for j, row := range slt.Rows {
   400  				if row != nil {
   401  					return false, nil, moerr.NewWrongValueCountOnRow(builder.GetContext(), j+1)
   402  				}
   403  			}
   404  		} else {
   405  			colCount := len(insertColumns)
   406  			for j, row := range slt.Rows {
   407  				if len(row) != colCount {
   408  					return false, nil, moerr.NewWrongValueCountOnRow(builder.GetContext(), j+1)
   409  				}
   410  			}
   411  		}
   412  
   413  		// example1:insert into a values ();
   414  		// but it does not work at the case:
   415  		// insert into a(a) values (); insert into a values (0),();
   416  		if isAllDefault && syntaxHasColumnNames {
   417  			return false, nil, moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns")
   418  		}
   419  		err = buildValueScan(isAllDefault, info, builder, bindCtx, tableDef, slt, insertColumns, colToIdx, stmt.OnDuplicateUpdate)
   420  		if err != nil {
   421  			return false, nil, err
   422  		}
   423  
   424  	case *tree.SelectClause:
   425  		astSlt = stmt.Rows
   426  
   427  		subCtx := NewBindContext(builder, bindCtx)
   428  		info.rootId, err = builder.buildSelect(astSlt, subCtx, false)
   429  		if err != nil {
   430  			return false, nil, err
   431  		}
   432  
   433  	case *tree.ParenSelect:
   434  		astSlt = slt.Select
   435  
   436  		subCtx := NewBindContext(builder, bindCtx)
   437  		info.rootId, err = builder.buildSelect(astSlt, subCtx, false)
   438  		if err != nil {
   439  			return false, nil, err
   440  		}
   441  
   442  	default:
   443  		return false, nil, moerr.NewInvalidInput(builder.GetContext(), "insert has unknown select statement")
   444  	}
   445  
   446  	err = builder.addBinding(info.rootId, tree.AliasClause{
   447  		Alias: derivedTableName,
   448  	}, bindCtx)
   449  	if err != nil {
   450  		return false, nil, err
   451  	}
   452  
   453  	lastNode := builder.qry.Nodes[info.rootId]
   454  	if len(insertColumns) != len(lastNode.ProjectList) {
   455  		return false, nil, moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns")
   456  	}
   457  
   458  	tag := builder.qry.Nodes[info.rootId].BindingTags[0]
   459  	info.derivedTableId = info.rootId
   460  	oldProject := append([]*Expr{}, lastNode.ProjectList...)
   461  
   462  	insertColToExpr := make(map[string]*Expr)
   463  	for i, column := range insertColumns {
   464  		colIdx := colToIdx[column]
   465  		projExpr := &plan.Expr{
   466  			Typ: oldProject[i].Typ,
   467  			Expr: &plan.Expr_Col{
   468  				Col: &plan.ColRef{
   469  					RelPos: tag,
   470  					ColPos: int32(i),
   471  				},
   472  			},
   473  		}
   474  		if tableDef.Cols[colIdx].Typ.Id == int32(types.T_enum) {
   475  			projExpr, err = funcCastForEnumType(builder.GetContext(), projExpr, tableDef.Cols[colIdx].Typ)
   476  			if err != nil {
   477  				return false, nil, err
   478  			}
   479  		} else {
   480  			projExpr, err = forceCastExpr(builder.GetContext(), projExpr, tableDef.Cols[colIdx].Typ)
   481  			if err != nil {
   482  				return false, nil, err
   483  			}
   484  		}
   485  		insertColToExpr[column] = projExpr
   486  	}
   487  
   488  	// create table t(a int, b int unique key);
   489  	// insert into t(a) values (1);  -> isInsertWithoutUniqueKey = true,  then we do not need a plan to insert unique_key_hidden_table;
   490  	// create table t(a int, b int unique key auto_increment)	-> isInsertWithoutUniqueKey is allways false
   491  	// create table t(a int, b int unique key default 10) 		-> isInsertWithoutUniqueKey is allways false
   492  	for _, idx := range tableDef.Indexes {
   493  		if idx.Unique {
   494  			withoutUniqueCol := true
   495  			for _, name := range idx.Parts {
   496  				_, ok := insertColToExpr[name]
   497  				if ok {
   498  					withoutUniqueCol = false
   499  					break
   500  				} else {
   501  					// insert without unique
   502  					// then need check col is not auto_incr & default is not null
   503  					col := tableDef.Cols[tableDef.Name2ColIndex[name]]
   504  					if col.Typ.AutoIncr || (col.Default.Expr != nil && !isNullExpr(col.Default.Expr)) {
   505  						withoutUniqueCol = false
   506  						break
   507  					}
   508  				}
   509  			}
   510  			insertWithoutUniqueKeyMap[idx.IndexName] = withoutUniqueCol
   511  		}
   512  	}
   513  
   514  	// have tables : t1(a default 0, b int, pk(a,b)) ,  t2(j int,k int)
   515  	// rewrite 'insert into t1 select * from t2' to
   516  	// select 'select _t.j, _t.k from (select * from t2) _t(j,k)
   517  	// --------
   518  	// rewrite 'insert into t1(b) values (1)' to
   519  	// select 'select 0, _t.column_0 from (select * from values (1)) _t(column_0)
   520  	projectList := make([]*Expr, 0, len(tableDef.Cols))
   521  	for _, col := range tableDef.Cols {
   522  		if oldExpr, exists := insertColToExpr[col.Name]; exists {
   523  			projectList = append(projectList, oldExpr)
   524  		} else {
   525  			defExpr, err := getDefaultExpr(builder.GetContext(), col)
   526  			if err != nil {
   527  				return false, nil, err
   528  			}
   529  
   530  			if col.Typ.AutoIncr && col.Name == tableDef.Pkey.PkeyColName {
   531  				ifExistAutoPkCol = true
   532  			}
   533  
   534  			projectList = append(projectList, defExpr)
   535  		}
   536  	}
   537  
   538  	// append ProjectNode
   539  	projectCtx := NewBindContext(builder, bindCtx)
   540  	lastTag := builder.genNewTag()
   541  	info.rootId = builder.appendNode(&plan.Node{
   542  		NodeType:    plan.Node_PROJECT,
   543  		ProjectList: projectList,
   544  		Children:    []int32{info.rootId},
   545  		BindingTags: []int32{lastTag},
   546  	}, projectCtx)
   547  
   548  	info.projectList = make([]*Expr, 0, len(projectList))
   549  	info.derivedTableId = info.rootId
   550  	for i, e := range projectList {
   551  		info.projectList = append(info.projectList, &plan.Expr{
   552  			Typ: e.Typ,
   553  			Expr: &plan.Expr_Col{
   554  				Col: &plan.ColRef{
   555  					RelPos: lastTag,
   556  					ColPos: int32(i),
   557  				},
   558  			},
   559  		})
   560  	}
   561  	info.idx = int32(len(info.projectList))
   562  
   563  	// if insert with on duplicate key . need append a join node
   564  	// create table t1 (a int primary key, b int unique key, c int);
   565  	// insert into t1 values (1,1,3),(2,2,3) on duplicate key update a=a+1, b=b-2;
   566  	// rewrite to : select _t.*, t1.a, t1.b,t1.c, t1.row_id from
   567  	//				(select * from values (1,1,3),(2,2,3)) _t(a,b,c) left join t1 on _t.a=t1.a or _t.b=t1.b
   568  	if len(stmt.OnDuplicateUpdate) > 0 {
   569  		isIgnore := len(stmt.OnDuplicateUpdate) == 1 && stmt.OnDuplicateUpdate[0] == nil
   570  		if isIgnore {
   571  			stmt.OnDuplicateUpdate = nil
   572  		}
   573  
   574  		rightTableDef := DeepCopyTableDef(tableDef, true)
   575  		rightObjRef := DeepCopyObjectRef(tableObjRef)
   576  		uniqueCols := GetUniqueColAndIdxFromTableDef(rightTableDef)
   577  		if rightTableDef.Pkey != nil && rightTableDef.Pkey.PkeyColName == catalog.CPrimaryKeyColName {
   578  			// rightTableDef.Cols = append(rightTableDef.Cols, MakeHiddenColDefByName(catalog.CPrimaryKeyColName))
   579  			rightTableDef.Cols = append(rightTableDef.Cols, rightTableDef.Pkey.CompPkeyCol)
   580  		}
   581  		if rightTableDef.ClusterBy != nil && util.JudgeIsCompositeClusterByColumn(rightTableDef.ClusterBy.Name) {
   582  			// rightTableDef.Cols = append(rightTableDef.Cols, MakeHiddenColDefByName(rightTableDef.ClusterBy.Name))
   583  			rightTableDef.Cols = append(rightTableDef.Cols, rightTableDef.ClusterBy.CompCbkeyCol)
   584  		}
   585  		rightTableDef.Cols = append(rightTableDef.Cols, MakeRowIdColDef())
   586  		rightTableDef.Name2ColIndex = map[string]int32{}
   587  		for i, col := range rightTableDef.Cols {
   588  			rightTableDef.Name2ColIndex[col.Name] = int32(i)
   589  		}
   590  
   591  		// if table have unique columns, we do the rewrite. if not, do nothing(do not throw error)
   592  		if len(uniqueCols) > 0 {
   593  
   594  			joinCtx := NewBindContext(builder, bindCtx)
   595  			rightCtx := NewBindContext(builder, joinCtx)
   596  			rightId := builder.appendNode(&plan.Node{
   597  				NodeType:    plan.Node_TABLE_SCAN,
   598  				ObjRef:      rightObjRef,
   599  				TableDef:    rightTableDef,
   600  				BindingTags: []int32{builder.genNewTag()},
   601  			}, rightCtx)
   602  			rightTag := builder.qry.Nodes[rightId].BindingTags[0]
   603  			baseNodeTag := builder.qry.Nodes[info.rootId].BindingTags[0]
   604  
   605  			// get update cols
   606  			updateCols := make(map[string]tree.Expr)
   607  			for _, updateExpr := range stmt.OnDuplicateUpdate {
   608  				col := updateExpr.Names[0].Parts[0]
   609  				updateCols[col] = updateExpr.Expr
   610  			}
   611  
   612  			var defExpr *Expr
   613  			idxs := make([]int32, len(rightTableDef.Cols))
   614  			updateExprs := make(map[string]*Expr)
   615  			for i, col := range rightTableDef.Cols {
   616  				info.idx = info.idx + 1
   617  				idxs[i] = info.idx
   618  				if updateExpr, exists := updateCols[col.Name]; exists {
   619  					binder := NewUpdateBinder(builder.GetContext(), nil, nil, rightTableDef.Cols)
   620  					binder.builder = builder
   621  					if _, ok := updateExpr.(*tree.DefaultVal); ok {
   622  						defExpr, err = getDefaultExpr(builder.GetContext(), col)
   623  						if err != nil {
   624  							return false, nil, err
   625  						}
   626  					} else {
   627  						defExpr, err = binder.BindExpr(updateExpr, 0, true)
   628  						if err != nil {
   629  							return false, nil, err
   630  						}
   631  					}
   632  					defExpr, err = forceCastExpr(builder.GetContext(), defExpr, col.Typ)
   633  					if err != nil {
   634  						return false, nil, err
   635  					}
   636  					updateExprs[col.Name] = defExpr
   637  				}
   638  				info.projectList = append(info.projectList, &plan.Expr{
   639  					Typ: col.Typ,
   640  					Expr: &plan.Expr_Col{
   641  						Col: &plan.ColRef{
   642  							RelPos: rightTag,
   643  							ColPos: int32(i),
   644  						},
   645  					},
   646  				})
   647  			}
   648  
   649  			// get join condition
   650  			var joinConds *Expr
   651  			joinIdx := 0
   652  			for _, uniqueColMap := range uniqueCols {
   653  				var condExpr *Expr
   654  				condIdx := int(0)
   655  				for _, colIdx := range uniqueColMap {
   656  					col := rightTableDef.Cols[colIdx]
   657  					leftExpr := &Expr{
   658  						Typ: col.Typ,
   659  						Expr: &plan.Expr_Col{
   660  							Col: &plan.ColRef{
   661  								RelPos: baseNodeTag,
   662  								ColPos: int32(colIdx),
   663  							},
   664  						},
   665  					}
   666  					rightExpr := &plan.Expr{
   667  						Typ: col.Typ,
   668  						Expr: &plan.Expr_Col{
   669  							Col: &plan.ColRef{
   670  								RelPos: rightTag,
   671  								ColPos: int32(colIdx),
   672  							},
   673  						},
   674  					}
   675  					eqExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{leftExpr, rightExpr})
   676  					if err != nil {
   677  						return false, nil, err
   678  					}
   679  					if condIdx == 0 {
   680  						condExpr = eqExpr
   681  					} else {
   682  						condExpr, err = BindFuncExprImplByPlanExpr(builder.GetContext(), "and", []*Expr{condExpr, eqExpr})
   683  						if err != nil {
   684  							return false, nil, err
   685  						}
   686  					}
   687  					condIdx++
   688  				}
   689  
   690  				if joinIdx == 0 {
   691  					joinConds = condExpr
   692  				} else {
   693  					joinConds, err = BindFuncExprImplByPlanExpr(builder.GetContext(), "or", []*Expr{joinConds, condExpr})
   694  					if err != nil {
   695  						return false, nil, err
   696  					}
   697  				}
   698  				joinIdx++
   699  			}
   700  
   701  			// append join node
   702  			leftCtx := builder.ctxByNode[info.rootId]
   703  			err = joinCtx.mergeContexts(builder.GetContext(), leftCtx, rightCtx)
   704  			if err != nil {
   705  				return false, nil, err
   706  			}
   707  			newRootId := builder.appendNode(&plan.Node{
   708  				NodeType: plan.Node_JOIN,
   709  				Children: []int32{info.rootId, rightId},
   710  				JoinType: plan.Node_LEFT,
   711  				OnList:   []*Expr{joinConds},
   712  			}, joinCtx)
   713  			bindCtx.binder = NewTableBinder(builder, bindCtx)
   714  			info.rootId = newRootId
   715  			info.onDuplicateIdx = idxs
   716  			info.onDuplicateExpr = updateExprs
   717  			info.onDuplicateNeedAgg = len(uniqueCols) > 1
   718  			info.onDuplicateIsIgnore = isIgnore
   719  
   720  			// append ProjectNode
   721  			info.rootId = builder.appendNode(&plan.Node{
   722  				NodeType:    plan.Node_PROJECT,
   723  				ProjectList: info.projectList,
   724  				Children:    []int32{info.rootId},
   725  				BindingTags: []int32{builder.genNewTag()},
   726  			}, bindCtx)
   727  			bindCtx.results = info.projectList
   728  		}
   729  	}
   730  
   731  	return ifExistAutoPkCol, insertWithoutUniqueKeyMap, nil
   732  }
   733  
   734  func deleteToSelect(builder *QueryBuilder, bindCtx *BindContext, node *tree.Delete, haveConstraint bool, tblInfo *dmlTableInfo) (int32, error) {
   735  	var selectList []tree.SelectExpr
   736  	fromTables := &tree.From{}
   737  
   738  	getResolveExpr := func(alias string) {
   739  		var ret *tree.UnresolvedName
   740  		if haveConstraint {
   741  			defIdx := tblInfo.alias[alias]
   742  			for _, col := range tblInfo.tableDefs[defIdx].Cols {
   743  				ret, _ = tree.NewUnresolvedName(builder.GetContext(), alias, col.Name)
   744  				selectList = append(selectList, tree.SelectExpr{
   745  					Expr: ret,
   746  				})
   747  			}
   748  		} else {
   749  			defIdx := tblInfo.alias[alias]
   750  			ret, _ = tree.NewUnresolvedName(builder.GetContext(), alias, catalog.Row_ID)
   751  			selectList = append(selectList, tree.SelectExpr{
   752  				Expr: ret,
   753  			})
   754  			pkName := getTablePriKeyName(tblInfo.tableDefs[defIdx].Pkey)
   755  			if pkName != "" {
   756  				ret, _ = tree.NewUnresolvedName(builder.GetContext(), alias, pkName)
   757  				selectList = append(selectList, tree.SelectExpr{
   758  					Expr: ret,
   759  				})
   760  			}
   761  		}
   762  	}
   763  
   764  	for _, tbl := range node.Tables {
   765  		if aliasTbl, ok := tbl.(*tree.AliasedTableExpr); ok {
   766  			alias := string(aliasTbl.As.Alias)
   767  			if alias != "" {
   768  				getResolveExpr(alias)
   769  			} else {
   770  				astTbl := aliasTbl.Expr.(*tree.TableName)
   771  				getResolveExpr(string(astTbl.ObjectName))
   772  			}
   773  		} else if astTbl, ok := tbl.(*tree.TableName); ok {
   774  			getResolveExpr(string(astTbl.ObjectName))
   775  		}
   776  	}
   777  
   778  	if node.TableRefs != nil {
   779  		fromTables.Tables = node.TableRefs
   780  	} else {
   781  		fromTables.Tables = node.Tables
   782  	}
   783  
   784  	astSelect := &tree.Select{
   785  		Select: &tree.SelectClause{
   786  			Distinct: false,
   787  			Exprs:    selectList,
   788  			From:     fromTables,
   789  			Where:    node.Where,
   790  		},
   791  		OrderBy: node.OrderBy,
   792  		Limit:   node.Limit,
   793  		With:    node.With,
   794  	}
   795  	// ftCtx := tree.NewFmtCtx(dialectType)
   796  	// astSelect.Format(ftCtx)
   797  	// sql := ftCtx.String()
   798  	// fmt.Print(sql)
   799  
   800  	return builder.buildSelect(astSelect, bindCtx, false)
   801  }
   802  
   803  func checkNotNull(ctx context.Context, expr *Expr, tableDef *TableDef, col *ColDef) error {
   804  	isConstantNull := false
   805  	if ef, ok := expr.Expr.(*plan.Expr_Lit); ok {
   806  		isConstantNull = ef.Lit.Isnull
   807  	}
   808  	if !isConstantNull {
   809  		return nil
   810  	}
   811  	if col.Default != nil && !col.Default.NullAbility {
   812  		return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", col.Name))
   813  	}
   814  
   815  	// if col.NotNull {
   816  	// 	return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", col.Name))
   817  	// }
   818  
   819  	// if (col.Primary && !col.Typ.AutoIncr) ||
   820  	// 	(col.Default != nil && !col.Default.NullAbility) {
   821  	// 	return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", col.Name))
   822  	// }
   823  
   824  	// if tableDef.Pkey != nil && len(tableDef.Pkey.Names) > 1 {
   825  	// 	names := tableDef.Pkey.Names
   826  	// 	for _, name := range names {
   827  	// 		if name == col.Name {
   828  	// 			return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", name))
   829  	// 		}
   830  	// 	}
   831  	// }
   832  
   833  	return nil
   834  }
   835  
   836  var ForceCastExpr = forceCastExpr
   837  
   838  func forceCastExpr2(ctx context.Context, expr *Expr, t2 types.Type, targetType *plan.Expr) (*Expr, error) {
   839  	if targetType.Typ.Id == 0 {
   840  		return expr, nil
   841  	}
   842  	t1 := makeTypeByPlan2Expr(expr)
   843  	if t1.Eq(t2) {
   844  		return expr, nil
   845  	}
   846  
   847  	targetType.Typ.NotNullable = expr.Typ.NotNullable
   848  	fGet, err := function.GetFunctionByName(ctx, "cast", []types.Type{t1, t2})
   849  	if err != nil {
   850  		return nil, err
   851  	}
   852  	return &plan.Expr{
   853  		Expr: &plan.Expr_F{
   854  			F: &plan.Function{
   855  				Func: &ObjectRef{Obj: fGet.GetEncodedOverloadID(), ObjName: "cast"},
   856  				Args: []*Expr{expr, targetType},
   857  			},
   858  		},
   859  		Typ: targetType.Typ,
   860  	}, nil
   861  }
   862  
   863  func forceCastExpr(ctx context.Context, expr *Expr, targetType Type) (*Expr, error) {
   864  	if targetType.Id == 0 {
   865  		return expr, nil
   866  	}
   867  	t1, t2 := makeTypeByPlan2Expr(expr), makeTypeByPlan2Type(targetType)
   868  	if t1.Eq(t2) {
   869  		return expr, nil
   870  	}
   871  
   872  	targetType.NotNullable = expr.Typ.NotNullable
   873  	fGet, err := function.GetFunctionByName(ctx, "cast", []types.Type{t1, t2})
   874  	if err != nil {
   875  		return nil, err
   876  	}
   877  	t := &plan.Expr{
   878  		Typ: targetType,
   879  		Expr: &plan.Expr_T{
   880  			T: &plan.TargetType{},
   881  		},
   882  	}
   883  	return &plan.Expr{
   884  		Expr: &plan.Expr_F{
   885  			F: &plan.Function{
   886  				Func: &ObjectRef{Obj: fGet.GetEncodedOverloadID(), ObjName: "cast"},
   887  				Args: []*Expr{expr, t},
   888  			},
   889  		},
   890  		Typ: targetType,
   891  	}, nil
   892  }
   893  
   894  func buildValueScan(
   895  	isAllDefault bool,
   896  	info *dmlSelectInfo,
   897  	builder *QueryBuilder,
   898  	bindCtx *BindContext,
   899  	tableDef *TableDef,
   900  	slt *tree.ValuesClause,
   901  	updateColumns []string,
   902  	colToIdx map[string]int,
   903  	OnDuplicateUpdate tree.UpdateExprs,
   904  ) error {
   905  	var err error
   906  
   907  	proc := builder.compCtx.GetProcess()
   908  	lastTag := builder.genNewTag()
   909  	colCount := len(updateColumns)
   910  	rowsetData := &plan.RowsetData{
   911  		Cols: make([]*plan.ColData, colCount),
   912  	}
   913  	for i := 0; i < colCount; i++ {
   914  		rowsetData.Cols[i] = new(plan.ColData)
   915  	}
   916  	valueScanTableDef := &plan.TableDef{
   917  		TblId: 0,
   918  		Name:  "",
   919  		Cols:  make([]*plan.ColDef, colCount),
   920  	}
   921  	projectList := make([]*Expr, colCount)
   922  	bat := batch.NewWithSize(len(updateColumns))
   923  
   924  	for i, colName := range updateColumns {
   925  		col := tableDef.Cols[colToIdx[colName]]
   926  		colTyp := makeTypeByPlan2Type(col.Typ)
   927  		vec := proc.GetVector(colTyp)
   928  		bat.Vecs[i] = vec
   929  		targetTyp := &plan.Expr{
   930  			Typ: col.Typ,
   931  			Expr: &plan.Expr_T{
   932  				T: &plan.TargetType{},
   933  			},
   934  		}
   935  		var defExpr *Expr
   936  		if isAllDefault {
   937  			defExpr, err := getDefaultExpr(builder.GetContext(), col)
   938  			if err != nil {
   939  				return err
   940  			}
   941  			defExpr, err = forceCastExpr2(builder.GetContext(), defExpr, colTyp, targetTyp)
   942  			if err != nil {
   943  				return err
   944  			}
   945  			for j := range slt.Rows {
   946  				if err := vector.AppendBytes(vec, nil, true, proc.Mp()); err != nil {
   947  					bat.Clean(proc.Mp())
   948  					return err
   949  				}
   950  				rowsetData.Cols[i].Data = append(rowsetData.Cols[i].Data, &plan.RowsetExpr{
   951  					Pos:    -1,
   952  					RowPos: int32(j),
   953  					Expr:   defExpr,
   954  				})
   955  			}
   956  		} else {
   957  			binder := NewDefaultBinder(builder.GetContext(), nil, nil, col.Typ, nil)
   958  			binder.builder = builder
   959  			for j, r := range slt.Rows {
   960  				if nv, ok := r[i].(*tree.NumVal); ok {
   961  					canInsert, err := util.SetInsertValue(proc, nv, vec)
   962  					if err != nil {
   963  						bat.Clean(proc.Mp())
   964  						return err
   965  					}
   966  					if canInsert {
   967  						continue
   968  					}
   969  				}
   970  
   971  				if err := vector.AppendBytes(vec, nil, true, proc.Mp()); err != nil {
   972  					bat.Clean(proc.Mp())
   973  					return err
   974  				}
   975  				if _, ok := r[i].(*tree.DefaultVal); ok {
   976  					defExpr, err = getDefaultExpr(builder.GetContext(), col)
   977  					if err != nil {
   978  						bat.Clean(proc.Mp())
   979  						return err
   980  					}
   981  				} else if nv, ok := r[i].(*tree.ParamExpr); ok {
   982  					if !builder.isPrepareStatement {
   983  						bat.Clean(proc.Mp())
   984  						return moerr.NewInvalidInput(builder.GetContext(), "only prepare statement can use ? expr")
   985  					}
   986  					rowsetData.Cols[i].Data = append(rowsetData.Cols[i].Data, &plan.RowsetExpr{
   987  						RowPos: int32(j),
   988  						Pos:    int32(nv.Offset),
   989  						Expr: &plan.Expr{
   990  							Typ: constTextType,
   991  							Expr: &plan.Expr_P{
   992  								P: &plan.ParamRef{
   993  									Pos: int32(nv.Offset),
   994  								},
   995  							},
   996  						},
   997  					})
   998  					continue
   999  				} else {
  1000  					defExpr, err = binder.BindExpr(r[i], 0, true)
  1001  					if err != nil {
  1002  						bat.Clean(proc.Mp())
  1003  						return err
  1004  					}
  1005  					if col.Typ.Id == int32(types.T_enum) {
  1006  						defExpr, err = funcCastForEnumType(builder.GetContext(), defExpr, col.Typ)
  1007  						if err != nil {
  1008  							bat.Clean(proc.Mp())
  1009  							return err
  1010  						}
  1011  					}
  1012  				}
  1013  				defExpr, err = forceCastExpr2(builder.GetContext(), defExpr, colTyp, targetTyp)
  1014  				if err != nil {
  1015  					return err
  1016  				}
  1017  				if nv, ok := r[i].(*tree.ParamExpr); ok {
  1018  					if !builder.isPrepareStatement {
  1019  						bat.Clean(proc.Mp())
  1020  						return moerr.NewInvalidInput(builder.GetContext(), "only prepare statement can use ? expr")
  1021  					}
  1022  					rowsetData.Cols[i].Data = append(rowsetData.Cols[i].Data, &plan.RowsetExpr{
  1023  						RowPos: int32(j),
  1024  						Pos:    int32(nv.Offset),
  1025  						Expr:   defExpr,
  1026  					})
  1027  					continue
  1028  				}
  1029  				rowsetData.Cols[i].Data = append(rowsetData.Cols[i].Data, &plan.RowsetExpr{
  1030  					Pos:    -1,
  1031  					RowPos: int32(j),
  1032  					Expr:   defExpr,
  1033  				})
  1034  			}
  1035  		}
  1036  		colName := fmt.Sprintf("column_%d", i) // like MySQL
  1037  		valueScanTableDef.Cols[i] = &plan.ColDef{
  1038  			ColId: 0,
  1039  			Name:  colName,
  1040  			Typ:   col.Typ,
  1041  		}
  1042  		expr := &plan.Expr{
  1043  			Typ: col.Typ,
  1044  			Expr: &plan.Expr_Col{
  1045  				Col: &plan.ColRef{
  1046  					RelPos: lastTag,
  1047  					ColPos: int32(i),
  1048  				},
  1049  			},
  1050  		}
  1051  		projectList[i] = expr
  1052  	}
  1053  
  1054  	onUpdateExprs := make([]*plan.Expr, 0)
  1055  	if builder.isPrepareStatement && !(len(OnDuplicateUpdate) == 1 && OnDuplicateUpdate[0] == nil) {
  1056  		for _, expr := range OnDuplicateUpdate {
  1057  			var updateExpr *plan.Expr
  1058  			col := tableDef.Cols[colToIdx[expr.Names[0].Parts[0]]]
  1059  			if nv, ok := expr.Expr.(*tree.ParamExpr); ok {
  1060  				updateExpr = &plan.Expr{
  1061  					Typ: constTextType,
  1062  					Expr: &plan.Expr_P{
  1063  						P: &plan.ParamRef{
  1064  							Pos: int32(nv.Offset),
  1065  						},
  1066  					},
  1067  				}
  1068  			} else if nv, ok := expr.Expr.(*tree.FuncExpr); ok {
  1069  				if checkExprHasParamExpr(nv.Exprs) {
  1070  					binder := NewDefaultBinder(builder.GetContext(), nil, nil, col.Typ, nil)
  1071  					binder.builder = builder
  1072  					binder.ctx = bindCtx
  1073  					updateExpr, err = binder.BindExpr(nv, 0, true)
  1074  					if err != nil {
  1075  						return err
  1076  					}
  1077  				}
  1078  			} else if nv, ok := expr.Expr.(*tree.BinaryExpr); ok {
  1079  				if checkExprHasParamExpr([]tree.Expr{nv.Right}) {
  1080  					binder := NewDefaultBinder(builder.GetContext(), nil, nil, col.Typ, nil)
  1081  					binder.builder = builder
  1082  					binder.ctx = bindCtx
  1083  					updateExpr, err = binder.BindExpr(nv.Right, 0, true)
  1084  					if err != nil {
  1085  						return err
  1086  					}
  1087  				}
  1088  			}
  1089  			if updateExpr != nil {
  1090  				onUpdateExprs = append(onUpdateExprs, updateExpr)
  1091  			}
  1092  		}
  1093  	}
  1094  	bat.SetRowCount(len(slt.Rows))
  1095  	rowsetData.RowCount = int32(len(slt.Rows))
  1096  	nodeId, _ := uuid.NewV7()
  1097  	scanNode := &plan.Node{
  1098  		NodeType:      plan.Node_VALUE_SCAN,
  1099  		RowsetData:    rowsetData,
  1100  		TableDef:      valueScanTableDef,
  1101  		BindingTags:   []int32{lastTag},
  1102  		Uuid:          nodeId[:],
  1103  		OnUpdateExprs: onUpdateExprs,
  1104  	}
  1105  	if builder.isPrepareStatement {
  1106  		proc.SetPrepareBatch(bat)
  1107  	} else {
  1108  		proc.SetValueScanBatch(nodeId, bat)
  1109  	}
  1110  	info.rootId = builder.appendNode(scanNode, bindCtx)
  1111  	err = builder.addBinding(info.rootId, tree.AliasClause{
  1112  		Alias: "_ValueScan",
  1113  	}, bindCtx)
  1114  	if err != nil {
  1115  		return err
  1116  	}
  1117  	lastTag = builder.genNewTag()
  1118  	info.rootId = builder.appendNode(&plan.Node{
  1119  		NodeType:    plan.Node_PROJECT,
  1120  		ProjectList: projectList,
  1121  		Children:    []int32{info.rootId},
  1122  		BindingTags: []int32{lastTag},
  1123  	}, bindCtx)
  1124  	return nil
  1125  }
  1126  
  1127  // if table have fk. then append join node & filter node
  1128  // sink_scan -> join -> filter
  1129  func appendForeignConstrantPlan(
  1130  	builder *QueryBuilder,
  1131  	bindCtx *BindContext,
  1132  	tableDef *TableDef,
  1133  	objRef *ObjectRef,
  1134  	sourceStep int32,
  1135  	isFkRecursionCall bool,
  1136  ) error {
  1137  	enabled, err := IsForeignKeyChecksEnabled(builder.compCtx)
  1138  	if err != nil {
  1139  		return err
  1140  	}
  1141  
  1142  	if enabled && !isFkRecursionCall && len(tableDef.Fkeys) > 0 {
  1143  		lastNodeId := appendSinkScanNode(builder, bindCtx, sourceStep)
  1144  
  1145  		lastNodeId, err := appendJoinNodeForParentFkCheck(builder, bindCtx, objRef, tableDef, lastNodeId)
  1146  		if err != nil {
  1147  			return err
  1148  		}
  1149  
  1150  		// if the all fk are fk self refer, the lastNodeId is -1.
  1151  		// skip fk self refer here
  1152  		if lastNodeId >= 0 {
  1153  			lastNode := builder.qry.Nodes[lastNodeId]
  1154  			beginIdx := len(lastNode.ProjectList) - len(tableDef.Fkeys)
  1155  
  1156  			// get filter exprs
  1157  			rowIdTyp := types.T_Rowid.ToType()
  1158  			filters := make([]*Expr, len(tableDef.Fkeys))
  1159  			errExpr := makePlan2StringConstExprWithType("Cannot add or update a child row: a foreign key constraint fails")
  1160  			for i := range tableDef.Fkeys {
  1161  				colExpr := &plan.Expr{
  1162  					Typ: makePlan2Type(&rowIdTyp),
  1163  					Expr: &plan.Expr_Col{
  1164  						Col: &plan.ColRef{
  1165  							ColPos: int32(beginIdx + i),
  1166  							// Name:   catalog.Row_ID,
  1167  						},
  1168  					},
  1169  				}
  1170  				nullCheckExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "isnotnull", []*Expr{colExpr})
  1171  				if err != nil {
  1172  					return err
  1173  				}
  1174  				filterExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "assert", []*Expr{nullCheckExpr, errExpr})
  1175  				if err != nil {
  1176  					return err
  1177  				}
  1178  				filters[i] = filterExpr
  1179  			}
  1180  
  1181  			// append filter node
  1182  			filterNode := &Node{
  1183  				NodeType:    plan.Node_FILTER,
  1184  				Children:    []int32{lastNodeId},
  1185  				FilterList:  filters,
  1186  				ProjectList: getProjectionByLastNode(builder, lastNodeId),
  1187  			}
  1188  			lastNodeId = builder.appendNode(filterNode, bindCtx)
  1189  
  1190  			builder.appendStep(lastNodeId)
  1191  		}
  1192  	}
  1193  	return nil
  1194  }
  1195  
  1196  // sink_scan -> Fuzzyfilter
  1197  // table_scan -----^
  1198  // there will be some cases that no need to check
  1199  //
  1200  //	case 1: For SQL that contains on duplicate update
  1201  //	case 2: the only primary key is auto increment type
  1202  func appendPrimaryConstrantPlan(
  1203  	builder *QueryBuilder,
  1204  	bindCtx *BindContext,
  1205  	tableDef *TableDef,
  1206  	objRef *ObjectRef,
  1207  	partitionExpr *Expr,
  1208  	pkFilterExprs []*Expr,
  1209  	indexSourceColTypes []*plan.Type,
  1210  	sourceStep int32,
  1211  	isUpdate bool,
  1212  	updatePkCol bool,
  1213  	fuzzymessage *OriginTableMessageForFuzzy,
  1214  ) error {
  1215  	var lastNodeId int32
  1216  	var err error
  1217  
  1218  	// need more comments here to explain checkCondition, for example, why updatePkCol is needed
  1219  	// we should not checkInsertPkDup any more, insert into t values (1) checkInsertPkDup is false, however it may still conflict with pk already exists
  1220  	if pkPos, pkTyp := getPkPos(tableDef, true); pkPos != -1 {
  1221  		// needCheck := true
  1222  		needCheck := !builder.qry.LoadTag
  1223  		useFuzzyFilter := config.CNPrimaryCheck
  1224  		if isUpdate {
  1225  			needCheck = updatePkCol
  1226  			useFuzzyFilter = false
  1227  		}
  1228  
  1229  		// insert stmt or update pk col, we need check insert pk dup
  1230  		if needCheck && !useFuzzyFilter {
  1231  			// make plan: sink_scan -> group_by -> filter  //check if pk is unique in rows
  1232  			lastNodeId = appendSinkScanNode(builder, bindCtx, sourceStep)
  1233  			pkColExpr := &plan.Expr{
  1234  				Typ: pkTyp,
  1235  				Expr: &plan.Expr_Col{
  1236  					Col: &plan.ColRef{
  1237  						ColPos: int32(pkPos),
  1238  						Name:   tableDef.Pkey.PkeyColName,
  1239  					},
  1240  				},
  1241  			}
  1242  			lastNodeId, err = appendAggCountGroupByColExpr(builder, bindCtx, lastNodeId, pkColExpr)
  1243  			if err != nil {
  1244  				return err
  1245  			}
  1246  
  1247  			countType := types.T_int64.ToType()
  1248  			countColExpr := &plan.Expr{
  1249  				Typ: makePlan2TypeValue(&countType),
  1250  				Expr: &plan.Expr_Col{
  1251  					Col: &plan.ColRef{
  1252  						Name: tableDef.Pkey.PkeyColName,
  1253  					},
  1254  				},
  1255  			}
  1256  
  1257  			eqCheckExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{MakePlan2Int64ConstExprWithType(1), countColExpr})
  1258  			if err != nil {
  1259  				return err
  1260  			}
  1261  			varcharType := types.T_varchar.ToType()
  1262  			varcharExpr, err := makePlan2CastExpr(builder.GetContext(), &Expr{
  1263  				Typ: tableDef.Cols[pkPos].Typ,
  1264  				Expr: &plan.Expr_Col{
  1265  					Col: &plan.ColRef{ColPos: 1, Name: tableDef.Cols[pkPos].Name},
  1266  				},
  1267  			}, makePlan2Type(&varcharType))
  1268  			if err != nil {
  1269  				return err
  1270  			}
  1271  			colTypes := string("")
  1272  			for i := range indexSourceColTypes {
  1273  				if types.T(indexSourceColTypes[i].Id).IsDecimal() {
  1274  					colTypes = colTypes + string(byte(indexSourceColTypes[i].Scale))
  1275  				} else {
  1276  					colTypes = colTypes + "0"
  1277  				}
  1278  			}
  1279  			filterExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "assert", []*Expr{eqCheckExpr, varcharExpr, makePlan2StringConstExprWithType(tableDef.Cols[pkPos].Name), makePlan2StringConstExprWithType(colTypes)})
  1280  			if err != nil {
  1281  				return err
  1282  			}
  1283  			filterNode := &Node{
  1284  				NodeType:   plan.Node_FILTER,
  1285  				Children:   []int32{lastNodeId},
  1286  				FilterList: []*Expr{filterExpr},
  1287  				IsEnd:      true,
  1288  			}
  1289  			lastNodeId = builder.appendNode(filterNode, bindCtx)
  1290  			builder.appendStep(lastNodeId)
  1291  		}
  1292  
  1293  		if needCheck && useFuzzyFilter {
  1294  			rfTag := builder.genNewMsgTag()
  1295  			probeExpr := &plan.Expr{
  1296  				Typ: pkTyp,
  1297  				Expr: &plan.Expr_Col{
  1298  					Col: &plan.ColRef{
  1299  						Name: tableDef.Pkey.PkeyColName,
  1300  					},
  1301  				},
  1302  			}
  1303  			// sink_scan
  1304  			sinkScanNode := &Node{
  1305  				NodeType:   plan.Node_SINK_SCAN,
  1306  				Stats:      &plan.Stats{},
  1307  				SourceStep: []int32{sourceStep},
  1308  				ProjectList: []*Expr{
  1309  					&plan.Expr{
  1310  						Typ: pkTyp,
  1311  						Expr: &plan.Expr_Col{
  1312  							Col: &plan.ColRef{
  1313  								ColPos: int32(pkPos),
  1314  								Name:   tableDef.Pkey.PkeyColName,
  1315  							},
  1316  						},
  1317  					},
  1318  				},
  1319  			}
  1320  			lastNodeId = builder.appendNode(sinkScanNode, bindCtx)
  1321  
  1322  			pkNameMap := make(map[string]int)
  1323  			for i, n := range tableDef.Pkey.Names {
  1324  				pkNameMap[n] = i
  1325  			}
  1326  			pkSize := len(tableDef.Pkey.Names)
  1327  			if pkSize > 1 {
  1328  				pkSize++
  1329  			}
  1330  			scanTableDef := DeepCopyTableDef(tableDef, false)
  1331  			scanTableDef.Cols = make([]*ColDef, pkSize)
  1332  			for _, col := range tableDef.Cols {
  1333  				if i, ok := pkNameMap[col.Name]; ok {
  1334  					scanTableDef.Cols[i] = DeepCopyColDef(col)
  1335  				} else if col.Name == scanTableDef.Pkey.PkeyColName {
  1336  					scanTableDef.Cols[pkSize-1] = DeepCopyColDef(col)
  1337  					break
  1338  				}
  1339  			}
  1340  
  1341  			if scanTableDef.Partition != nil && partitionExpr != nil {
  1342  				scanTableDef.Partition.PartitionExpression = partitionExpr
  1343  			}
  1344  
  1345  			scanNode := &plan.Node{
  1346  				NodeType: plan.Node_TABLE_SCAN,
  1347  				Stats:    &plan.Stats{},
  1348  				ObjRef:   objRef,
  1349  				TableDef: scanTableDef,
  1350  				ProjectList: []*Expr{{
  1351  					Typ: pkTyp,
  1352  					Expr: &plan.Expr_Col{
  1353  						Col: &ColRef{
  1354  							ColPos: int32(len(scanTableDef.Cols) - 1),
  1355  							Name:   tableDef.Pkey.PkeyColName,
  1356  						},
  1357  					},
  1358  				}},
  1359  				RuntimeFilterProbeList: []*plan.RuntimeFilterSpec{MakeRuntimeFilter(rfTag, false, 0, probeExpr)},
  1360  			}
  1361  
  1362  			var tableScanId int32
  1363  
  1364  			if len(pkFilterExprs) > 0 {
  1365  				var blockFilterList []*Expr
  1366  				scanNode.FilterList = pkFilterExprs
  1367  				blockFilterList = make([]*Expr, len(pkFilterExprs))
  1368  				for i, e := range pkFilterExprs {
  1369  					blockFilterList[i] = DeepCopyExpr(e)
  1370  				}
  1371  				tableScanId = builder.appendNode(scanNode, bindCtx)
  1372  				scanNode.BlockFilterList = blockFilterList
  1373  				scanNode.RuntimeFilterProbeList = nil // can not use both
  1374  			} else {
  1375  				tableScanId = builder.appendNode(scanNode, bindCtx)
  1376  				// temporary solution for the plan of dml go without optimizer
  1377  				// prevent table scan from running on multiple CNs.
  1378  				// because the runtime filter can only run on one now.
  1379  				scanNode.Stats = DefaultMinimalStats()
  1380  			}
  1381  
  1382  			// Perform partition pruning on the full table scan of the partitioned table in the insert statement
  1383  			if scanTableDef.Partition != nil && partitionExpr != nil {
  1384  				builder.partitionPrune(tableScanId)
  1385  			}
  1386  
  1387  			// fuzzy_filter
  1388  			fuzzyFilterNode := &Node{
  1389  				NodeType: plan.Node_FUZZY_FILTER,
  1390  				Children: []int32{tableScanId, lastNodeId}, // right table build hash
  1391  				TableDef: tableDef,
  1392  				ObjRef:   objRef,
  1393  			}
  1394  
  1395  			if fuzzymessage != nil {
  1396  				fuzzyFilterNode.Fuzzymessage = &plan.OriginTableMessageForFuzzy{
  1397  					ParentTableName:  fuzzymessage.ParentTableName,
  1398  					ParentUniqueCols: fuzzymessage.ParentUniqueCols,
  1399  				}
  1400  			}
  1401  
  1402  			if len(pkFilterExprs) == 0 {
  1403  				buildExpr := &plan.Expr{
  1404  					Typ: pkTyp,
  1405  					Expr: &plan.Expr_Col{
  1406  						Col: &plan.ColRef{
  1407  							RelPos: 0,
  1408  							ColPos: 0,
  1409  						},
  1410  					},
  1411  				}
  1412  				fuzzyFilterNode.RuntimeFilterBuildList = []*plan.RuntimeFilterSpec{MakeRuntimeFilter(rfTag, false, GetInFilterCardLimitOnPK(scanNode.Stats.TableCnt), buildExpr)}
  1413  			}
  1414  
  1415  			lastNodeId = builder.appendNode(fuzzyFilterNode, bindCtx)
  1416  			builder.appendStep(lastNodeId)
  1417  		}
  1418  	}
  1419  
  1420  	// The refactor that using fuzzy filter has not been completely finished, Update type Insert cannot directly use fuzzy filter for duplicate detection.
  1421  	//  so the original logic is retained. should be deleted later
  1422  	// make plan: sink_scan -> join -> filter	// check if pk is unique in rows & snapshot
  1423  	if config.CNPrimaryCheck {
  1424  		if pkPos, pkTyp := getPkPos(tableDef, true); pkPos != -1 {
  1425  			rfTag := builder.genNewMsgTag()
  1426  
  1427  			if isUpdate && updatePkCol { // update stmt && pk included in update cols
  1428  				lastNodeId = appendSinkScanNode(builder, bindCtx, sourceStep)
  1429  				scanTableDef := DeepCopyTableDef(tableDef, false)
  1430  
  1431  				rowIdIdx := len(tableDef.Cols)
  1432  				rowIdDef := MakeRowIdColDef()
  1433  				tableDef.Cols = append(tableDef.Cols, rowIdDef)
  1434  
  1435  				scanTableDef.Cols = []*plan.ColDef{DeepCopyColDef(tableDef.Cols[pkPos]), DeepCopyColDef(rowIdDef)}
  1436  
  1437  				scanPkExpr := &Expr{
  1438  					Typ: pkTyp,
  1439  					Expr: &plan.Expr_Col{
  1440  						Col: &ColRef{
  1441  							Name: tableDef.Pkey.PkeyColName,
  1442  						},
  1443  					},
  1444  				}
  1445  				scanRowIdExpr := &Expr{
  1446  					Typ: rowIdDef.Typ,
  1447  					Expr: &plan.Expr_Col{
  1448  						Col: &ColRef{
  1449  							ColPos: 1,
  1450  							Name:   rowIdDef.Name,
  1451  						},
  1452  					},
  1453  				}
  1454  
  1455  				probeExpr := &plan.Expr{
  1456  					Typ: pkTyp,
  1457  					Expr: &plan.Expr_Col{
  1458  						Col: &plan.ColRef{
  1459  							Name: tableDef.Pkey.PkeyColName,
  1460  						},
  1461  					},
  1462  				}
  1463  				scanNode := &Node{
  1464  					NodeType:               plan.Node_TABLE_SCAN,
  1465  					Stats:                  &plan.Stats{},
  1466  					ObjRef:                 objRef,
  1467  					TableDef:               scanTableDef,
  1468  					ProjectList:            []*Expr{scanPkExpr, scanRowIdExpr},
  1469  					RuntimeFilterProbeList: []*plan.RuntimeFilterSpec{MakeRuntimeFilter(rfTag, false, 0, probeExpr)},
  1470  				}
  1471  				rightId := builder.appendNode(scanNode, bindCtx)
  1472  
  1473  				pkColExpr := &Expr{
  1474  					Typ: pkTyp,
  1475  					Expr: &plan.Expr_Col{
  1476  						Col: &ColRef{
  1477  							RelPos: 1,
  1478  							ColPos: int32(pkPos),
  1479  							Name:   tableDef.Pkey.PkeyColName,
  1480  						},
  1481  					},
  1482  				}
  1483  				rightExpr := &Expr{
  1484  					Typ: pkTyp,
  1485  					Expr: &plan.Expr_Col{
  1486  						Col: &plan.ColRef{
  1487  							Name: tableDef.Pkey.PkeyColName,
  1488  						},
  1489  					},
  1490  				}
  1491  				condExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{pkColExpr, rightExpr})
  1492  				if err != nil {
  1493  					return err
  1494  				}
  1495  				rightRowIdExpr := &Expr{
  1496  					Typ: rowIdDef.Typ,
  1497  					Expr: &plan.Expr_Col{
  1498  						Col: &ColRef{
  1499  							ColPos: 1,
  1500  							Name:   rowIdDef.Name,
  1501  						},
  1502  					},
  1503  				}
  1504  				rowIdExpr := &Expr{
  1505  					Typ: rowIdDef.Typ,
  1506  					Expr: &plan.Expr_Col{
  1507  						Col: &ColRef{
  1508  							RelPos: 1,
  1509  							ColPos: int32(rowIdIdx),
  1510  							Name:   rowIdDef.Name,
  1511  						},
  1512  					},
  1513  				}
  1514  
  1515  				buildExpr := &plan.Expr{
  1516  					Typ: pkTyp,
  1517  					Expr: &plan.Expr_Col{
  1518  						Col: &plan.ColRef{
  1519  							RelPos: 0,
  1520  							ColPos: 0,
  1521  						},
  1522  					},
  1523  				}
  1524  				joinNode := &plan.Node{
  1525  					NodeType:               plan.Node_JOIN,
  1526  					Children:               []int32{rightId, lastNodeId},
  1527  					JoinType:               plan.Node_RIGHT,
  1528  					OnList:                 []*Expr{condExpr},
  1529  					ProjectList:            []*Expr{rowIdExpr, rightRowIdExpr, pkColExpr},
  1530  					RuntimeFilterBuildList: []*plan.RuntimeFilterSpec{MakeRuntimeFilter(rfTag, false, GetInFilterCardLimitOnPK(scanNode.Stats.TableCnt), buildExpr)},
  1531  				}
  1532  				lastNodeId = builder.appendNode(joinNode, bindCtx)
  1533  
  1534  				// append agg node.
  1535  				aggGroupBy := []*Expr{
  1536  					{
  1537  						Typ: rowIdExpr.Typ,
  1538  						Expr: &plan.Expr_Col{
  1539  							Col: &ColRef{
  1540  								ColPos: 0,
  1541  								Name:   catalog.Row_ID,
  1542  							},
  1543  						}},
  1544  					{
  1545  						Typ: rowIdExpr.Typ,
  1546  						Expr: &plan.Expr_Col{
  1547  							Col: &ColRef{
  1548  								ColPos: 1,
  1549  								Name:   catalog.Row_ID,
  1550  							},
  1551  						}},
  1552  					{
  1553  						Typ: pkColExpr.Typ,
  1554  						Expr: &plan.Expr_Col{
  1555  							Col: &ColRef{
  1556  								ColPos: 2,
  1557  								Name:   tableDef.Pkey.PkeyColName,
  1558  							},
  1559  						}},
  1560  				}
  1561  				aggProject := []*Expr{
  1562  					{
  1563  						Typ: rowIdExpr.Typ,
  1564  						Expr: &plan.Expr_Col{
  1565  							Col: &ColRef{
  1566  								RelPos: -1,
  1567  								ColPos: 0,
  1568  								Name:   catalog.Row_ID,
  1569  							},
  1570  						}},
  1571  					{
  1572  						Typ: rowIdExpr.Typ,
  1573  						Expr: &plan.Expr_Col{
  1574  							Col: &ColRef{
  1575  								RelPos: -1,
  1576  								ColPos: 1,
  1577  								Name:   catalog.Row_ID,
  1578  							},
  1579  						}},
  1580  					{
  1581  						Typ: pkColExpr.Typ,
  1582  						Expr: &plan.Expr_Col{
  1583  							Col: &ColRef{
  1584  								RelPos: -1,
  1585  								ColPos: 2,
  1586  								Name:   tableDef.Pkey.PkeyColName,
  1587  							},
  1588  						}},
  1589  				}
  1590  				aggNode := &Node{
  1591  					NodeType:    plan.Node_AGG,
  1592  					Children:    []int32{lastNodeId},
  1593  					GroupBy:     aggGroupBy,
  1594  					ProjectList: aggProject,
  1595  				}
  1596  				lastNodeId = builder.appendNode(aggNode, bindCtx)
  1597  
  1598  				// append filter node
  1599  				filterExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "not_in_rows", []*Expr{
  1600  					{
  1601  						Typ: rowIdExpr.Typ,
  1602  						Expr: &plan.Expr_Col{
  1603  							Col: &ColRef{ColPos: 1, Name: catalog.Row_ID},
  1604  						},
  1605  					},
  1606  					{
  1607  						Typ: rowIdExpr.Typ,
  1608  						Expr: &plan.Expr_Col{
  1609  							Col: &ColRef{ColPos: 0, Name: catalog.Row_ID},
  1610  						},
  1611  					},
  1612  				})
  1613  				if err != nil {
  1614  					return err
  1615  				}
  1616  				colExpr := &Expr{
  1617  					Typ: rowIdDef.Typ,
  1618  					Expr: &plan.Expr_Col{
  1619  						Col: &plan.ColRef{
  1620  							Name: rowIdDef.Name,
  1621  						},
  1622  					},
  1623  				}
  1624  
  1625  				lastNodeId = builder.appendNode(&Node{
  1626  					NodeType:   plan.Node_FILTER,
  1627  					Children:   []int32{lastNodeId},
  1628  					FilterList: []*Expr{filterExpr},
  1629  					ProjectList: []*Expr{
  1630  						colExpr,
  1631  						{
  1632  							Typ: tableDef.Cols[pkPos].Typ,
  1633  							Expr: &plan.Expr_Col{
  1634  								Col: &plan.ColRef{ColPos: 2, Name: tableDef.Cols[pkPos].Name},
  1635  							},
  1636  						},
  1637  					},
  1638  				}, bindCtx)
  1639  
  1640  				// append assert node
  1641  				isEmptyExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "isempty", []*Expr{colExpr})
  1642  				if err != nil {
  1643  					return err
  1644  				}
  1645  
  1646  				varcharType := types.T_varchar.ToType()
  1647  				varcharExpr, err := makePlan2CastExpr(builder.GetContext(), &Expr{
  1648  					Typ: tableDef.Cols[pkPos].Typ,
  1649  					Expr: &plan.Expr_Col{
  1650  						Col: &plan.ColRef{ColPos: 1, Name: tableDef.Cols[pkPos].Name},
  1651  					},
  1652  				}, makePlan2Type(&varcharType))
  1653  				if err != nil {
  1654  					return err
  1655  				}
  1656  				colTypes := string("")
  1657  				for i := range indexSourceColTypes {
  1658  					if types.T(indexSourceColTypes[i].Id).IsDecimal() {
  1659  						colTypes = colTypes + string(byte(indexSourceColTypes[i].Scale))
  1660  					} else {
  1661  						colTypes = colTypes + "0"
  1662  					}
  1663  				}
  1664  				assertExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "assert", []*Expr{isEmptyExpr, varcharExpr, makePlan2StringConstExprWithType(tableDef.Cols[pkPos].Name), makePlan2StringConstExprWithType(colTypes)})
  1665  				if err != nil {
  1666  					return err
  1667  				}
  1668  				lastNodeId = builder.appendNode(&Node{
  1669  					NodeType:   plan.Node_FILTER,
  1670  					Children:   []int32{lastNodeId},
  1671  					FilterList: []*Expr{assertExpr},
  1672  					IsEnd:      true,
  1673  				}, bindCtx)
  1674  				builder.appendStep(lastNodeId)
  1675  			}
  1676  		}
  1677  	}
  1678  
  1679  	return nil
  1680  }