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 // }