github.com/matrixorigin/matrixone@v1.2.0/pkg/sql/plan/build_update.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 plan
    16  
    17  import (
    18  	"go/constant"
    19  	"time"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/catalog"
    22  	"github.com/matrixorigin/matrixone/pkg/container/types"
    23  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    24  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    25  	v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2"
    26  )
    27  
    28  func buildTableUpdate(stmt *tree.Update, ctx CompilerContext, isPrepareStmt bool) (p *Plan, err error) {
    29  	start := time.Now()
    30  	defer func() {
    31  		v2.TxnStatementBuildUpdateHistogram.Observe(time.Since(start).Seconds())
    32  	}()
    33  	tblInfo, err := getUpdateTableInfo(ctx, stmt)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	// new logic
    38  	builder := NewQueryBuilder(plan.Query_SELECT, ctx, isPrepareStmt)
    39  	queryBindCtx := NewBindContext(builder, nil)
    40  	lastNodeId, updatePlanCtxs, err := selectUpdateTables(builder, queryBindCtx, stmt, tblInfo)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	err = rewriteUpdateQueryLastNode(builder, updatePlanCtxs, lastNodeId)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	sourceStep := builder.appendStep(lastNodeId)
    50  	query, err := builder.createQuery()
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	// if len(updatePlanCtxs) == 1 && updatePlanCtxs[0].updatePkCol {
    56  	// 	pkFilterExpr := getPkFilterExpr(builder, lastNodeId, updatePlanCtxs[0], tblInfo.tableDefs[0])
    57  	// 	updatePlanCtxs[0].pkFilterExprs = pkFilterExpr
    58  	// }
    59  
    60  	builder.qry.Steps = append(builder.qry.Steps[:sourceStep], builder.qry.Steps[sourceStep+1:]...)
    61  
    62  	// append sink node
    63  	lastNodeId = appendSinkNode(builder, queryBindCtx, lastNodeId)
    64  	sourceStep = builder.appendStep(lastNodeId)
    65  
    66  	beginIdx := 0
    67  	var detectSqls []string
    68  	for i, tableDef := range tblInfo.tableDefs {
    69  		upPlanCtx := updatePlanCtxs[i]
    70  		upPlanCtx.beginIdx = beginIdx
    71  		upPlanCtx.sourceStep = sourceStep
    72  
    73  		updateBindCtx := NewBindContext(builder, nil)
    74  		beginIdx = beginIdx + upPlanCtx.updateColLength + len(tableDef.Cols)
    75  		err = buildUpdatePlans(ctx, builder, updateBindCtx, upPlanCtx, false)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		putDmlPlanCtx(upPlanCtx)
    80  		sqls, err := genSqlsForCheckFKSelfRefer(ctx.GetContext(),
    81  			tblInfo.objRef[i].SchemaName, tableDef.Name, tableDef.Cols, tableDef.Fkeys)
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  		detectSqls = append(detectSqls, sqls...)
    86  	}
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	query.DetectSqls = detectSqls
    91  	reduceSinkSinkScanNodes(query)
    92  	ReCalcQueryStats(builder, query)
    93  	reCheckifNeedLockWholeTable(builder)
    94  	query.StmtType = plan.Query_UPDATE
    95  	return &Plan{
    96  		Plan: &plan.Plan_Query{
    97  			Query: query,
    98  		},
    99  	}, err
   100  }
   101  
   102  func isDefaultValExpr(e *Expr) bool {
   103  	if ce, ok := e.Expr.(*plan.Expr_Lit); ok {
   104  		_, isDefVal := ce.Lit.Value.(*plan.Literal_Defaultval)
   105  		return isDefVal
   106  	}
   107  	return false
   108  }
   109  
   110  func rewriteUpdateQueryLastNode(builder *QueryBuilder, planCtxs []*dmlPlanCtx, lastNodeId int32) error {
   111  	var err error
   112  
   113  	lastNode := builder.qry.Nodes[lastNodeId]
   114  
   115  	idx := 0
   116  	for _, planCtx := range planCtxs {
   117  		tableDef := planCtx.tableDef
   118  
   119  		for colIdx, col := range tableDef.Cols {
   120  			if offset, ok := planCtx.updateColPosMap[col.Name]; ok {
   121  				pos := idx + offset
   122  				posExpr := lastNode.ProjectList[pos]
   123  				if isDefaultValExpr(posExpr) { // set col = default
   124  					lastNode.ProjectList[pos], err = getDefaultExpr(builder.GetContext(), col)
   125  					if err != nil {
   126  						return err
   127  					}
   128  					posExpr = lastNode.ProjectList[pos]
   129  				}
   130  				err = checkNotNull(builder.GetContext(), posExpr, tableDef, col)
   131  				if err != nil {
   132  					return err
   133  				}
   134  				if col != nil && col.Typ.Id == int32(types.T_enum) {
   135  					lastNode.ProjectList[pos], err = funcCastForEnumType(builder.GetContext(), posExpr, col.Typ)
   136  					if err != nil {
   137  						return err
   138  					}
   139  				} else {
   140  					lastNode.ProjectList[pos], err = forceCastExpr(builder.GetContext(), posExpr, col.Typ)
   141  					if err != nil {
   142  						return err
   143  					}
   144  				}
   145  
   146  			} else {
   147  				pos := idx + colIdx
   148  				if col.OnUpdate != nil && col.OnUpdate.Expr != nil {
   149  					lastNode.ProjectList[pos] = col.OnUpdate.Expr
   150  				}
   151  
   152  				if col != nil && col.Typ.Id == int32(types.T_enum) {
   153  					lastNode.ProjectList[pos], err = funcCastForEnumType(builder.GetContext(), lastNode.ProjectList[pos], col.Typ)
   154  					if err != nil {
   155  						return err
   156  					}
   157  				} else {
   158  					lastNode.ProjectList[pos], err = forceCastExpr(builder.GetContext(), lastNode.ProjectList[pos], col.Typ)
   159  					if err != nil {
   160  						return err
   161  					}
   162  				}
   163  			}
   164  		}
   165  		idx = planCtx.updateColLength + len(tableDef.Cols)
   166  	}
   167  	return nil
   168  }
   169  
   170  func selectUpdateTables(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Update, tableInfo *dmlTableInfo) (int32, []*dmlPlanCtx, error) {
   171  	fromTables := &tree.From{
   172  		Tables: stmt.Tables,
   173  	}
   174  	var selectList []tree.SelectExpr
   175  
   176  	var aliasList = make([]string, len(tableInfo.alias))
   177  	for alias, i := range tableInfo.alias {
   178  		aliasList[i] = alias
   179  	}
   180  
   181  	updatePlanCtxs := make([]*dmlPlanCtx, len(aliasList))
   182  	for i, alias := range aliasList {
   183  		tableDef := tableInfo.tableDefs[i]
   184  		updateKeys := tableInfo.updateKeys[i]
   185  
   186  		// append  table.* to project list
   187  		rowIdPos := -1
   188  		for idx, col := range tableDef.Cols {
   189  			e, _ := tree.NewUnresolvedName(builder.GetContext(), alias, col.Name)
   190  			selectList = append(selectList, tree.SelectExpr{
   191  				Expr: e,
   192  			})
   193  			if col.Name == catalog.Row_ID {
   194  				rowIdPos = idx
   195  			}
   196  		}
   197  
   198  		// add update expr to project list
   199  		updateColPosMap := make(map[string]int)
   200  		offset := len(tableDef.Cols)
   201  		updatePkCol := false
   202  		updatePkColCount := 0
   203  		var pkNameMap = make(map[string]struct{})
   204  		if tableDef.Pkey != nil {
   205  			for _, pkName := range tableDef.Pkey.Names {
   206  				pkNameMap[pkName] = struct{}{}
   207  			}
   208  		}
   209  
   210  		for colName, updateKey := range updateKeys {
   211  			for _, coldef := range tableDef.Cols {
   212  				if coldef.Name == colName && coldef.Typ.Id == int32(types.T_enum) {
   213  					binder := NewDefaultBinder(builder.GetContext(), nil, nil, coldef.Typ, nil)
   214  					updateKeyExpr, err := binder.BindExpr(updateKey, 0, false)
   215  					if err != nil {
   216  						return 0, nil, err
   217  					}
   218  					exprs := []tree.Expr{
   219  						tree.NewNumValWithType(constant.MakeString(coldef.Typ.Enumvalues), coldef.Typ.Enumvalues, false, tree.P_char),
   220  						updateKey,
   221  					}
   222  					if updateKeyExpr.Typ.Id >= 20 && updateKeyExpr.Typ.Id <= 29 {
   223  						updateKey = &tree.FuncExpr{
   224  							Func:  tree.FuncName2ResolvableFunctionReference(tree.SetUnresolvedName(moEnumCastIndexValueToIndexFun)),
   225  							Type:  tree.FUNC_TYPE_DEFAULT,
   226  							Exprs: exprs,
   227  						}
   228  					} else {
   229  						updateKey = &tree.FuncExpr{
   230  							Func:  tree.FuncName2ResolvableFunctionReference(tree.SetUnresolvedName(moEnumCastValueToIndexFun)),
   231  							Type:  tree.FUNC_TYPE_DEFAULT,
   232  							Exprs: exprs,
   233  						}
   234  					}
   235  				}
   236  			}
   237  			selectList = append(selectList, tree.SelectExpr{
   238  				Expr: updateKey,
   239  			})
   240  			updateColPosMap[colName] = offset
   241  			if _, ok := pkNameMap[colName]; ok {
   242  				updatePkColCount++
   243  			}
   244  			offset++
   245  		}
   246  
   247  		// we don't known if update pk if tableDef.Pkey is nil. just set true and let check pk dup work
   248  		updatePkCol = updatePkColCount > 0
   249  
   250  		// append  table.* to project list
   251  		upPlanCtx := getDmlPlanCtx()
   252  		upPlanCtx.objRef = tableInfo.objRef[i]
   253  		upPlanCtx.tableDef = tableDef
   254  		upPlanCtx.updateColLength = len(updateKeys)
   255  		upPlanCtx.isMulti = tableInfo.isMulti
   256  		upPlanCtx.needAggFilter = tableInfo.needAggFilter
   257  		upPlanCtx.rowIdPos = rowIdPos
   258  		upPlanCtx.updateColPosMap = updateColPosMap
   259  		upPlanCtx.allDelTableIDs = map[uint64]struct{}{}
   260  		upPlanCtx.allDelTables = map[FkReferKey]struct{}{}
   261  		upPlanCtx.checkInsertPkDup = true
   262  		upPlanCtx.updatePkCol = updatePkCol
   263  
   264  		for idx, col := range tableDef.Cols {
   265  			// row_id、compPrimaryKey、clusterByKey will not inserted from old data
   266  			if col.Hidden && col.Name != catalog.FakePrimaryKeyColName {
   267  				continue
   268  			}
   269  			if offset, ok := updateColPosMap[col.Name]; ok {
   270  				upPlanCtx.insertColPos = append(upPlanCtx.insertColPos, offset)
   271  			} else {
   272  				upPlanCtx.insertColPos = append(upPlanCtx.insertColPos, idx)
   273  			}
   274  		}
   275  
   276  		updatePlanCtxs[i] = upPlanCtx
   277  	}
   278  
   279  	selectAst := &tree.Select{
   280  		Select: &tree.SelectClause{
   281  			Distinct: false,
   282  			Exprs:    selectList,
   283  			From:     fromTables,
   284  			Where:    stmt.Where,
   285  		},
   286  		OrderBy: stmt.OrderBy,
   287  		Limit:   stmt.Limit,
   288  		With:    stmt.With,
   289  	}
   290  
   291  	//ftCtx := tree.NewFmtCtx(dialect.MYSQL)
   292  	//selectAst.Format(ftCtx)
   293  	//sql := ftCtx.String()
   294  	//fmt.Print(sql)
   295  	lastNodeId, err := builder.buildSelect(selectAst, bindCtx, false)
   296  	if err != nil {
   297  		return -1, nil, err
   298  	}
   299  	return lastNodeId, updatePlanCtxs, nil
   300  }
   301  
   302  // todo:  seems this filter do not make any sense for check pk dup in update statement
   303  //        need more research
   304  // func getPkFilterExpr(builder *QueryBuilder, lastNodeId int32, upCtx *dmlPlanCtx, tableDef *TableDef) []*Expr {
   305  // 	node := builder.qry.Nodes[lastNodeId]
   306  // 	var pkNameMap = make(map[string]int)
   307  // 	for idx, pkName := range tableDef.Pkey.Names {
   308  // 		pkNameMap[pkName] = idx
   309  // 	}
   310  
   311  // 	filterExpr := make([]*Expr, len(tableDef.Pkey.Names))
   312  // 	for colName, colIdx := range upCtx.updateColPosMap {
   313  // 		if pkIdx, ok := pkNameMap[colName]; ok {
   314  // 			switch e := node.ProjectList[colIdx].Expr.(type) {
   315  // 			case *plan.Expr_C:
   316  // 			case *plan.Expr_F:
   317  // 				if e.F.Func.ObjName != "cast" {
   318  // 					return nil
   319  // 				}
   320  // 				if _, isConst := e.F.Args[0].Expr.(*plan.Expr_C); !isConst {
   321  // 					return nil
   322  // 				}
   323  // 			default:
   324  // 				return nil
   325  // 			}
   326  
   327  // 			expr, err := bindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{{
   328  // 				Typ: node.ProjectList[colIdx].Typ,
   329  // 				Expr: &plan.Expr_Col{
   330  // 					Col: &ColRef{
   331  // 						ColPos: int32(pkIdx),
   332  // 						Name:   tableDef.Pkey.PkeyColName,
   333  // 					},
   334  // 				},
   335  // 			}, node.ProjectList[colIdx]})
   336  // 			if err != nil {
   337  // 				return nil
   338  // 			}
   339  // 			filterExpr[pkIdx] = expr
   340  // 		}
   341  // 	}
   342  // 	return filterExpr
   343  // }