github.com/matrixorigin/matrixone@v0.7.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/matrixorigin/matrixone/pkg/catalog" 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 "github.com/matrixorigin/matrixone/pkg/container/types" 24 "github.com/matrixorigin/matrixone/pkg/pb/plan" 25 "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" 26 "github.com/matrixorigin/matrixone/pkg/sql/plan/function" 27 "github.com/matrixorigin/matrixone/pkg/sql/util" 28 ) 29 30 const derivedTableName = "_t" 31 32 type dmlSelectInfo struct { 33 typ string 34 35 projectList []*Expr 36 tblInfo *dmlTableInfo 37 idx int32 38 rootId int32 39 derivedTableId int32 40 41 onIdx []int32 //remove these row 42 onIdxTbl []*ObjectRef 43 44 onRestrict []int32 // check these, not all null then throw error 45 onRestrictTbl []*ObjectRef 46 47 onSet [][]int64 48 onSetTableDef []*TableDef 49 onSetRef []*ObjectRef 50 onSetUpdateCol []map[string]int32 // name=updated col.Name value=col position in TableDef.Cols 51 52 onCascade [][]int64 53 onCascadeTableDef []*TableDef 54 onCascadeRef []*ObjectRef 55 onCascadeUpdateCol []map[string]int32 // name=updated col.Name value=col position in TableDef.Cols 56 57 parentIdx []map[string]int32 58 } 59 60 type dmlTableInfo struct { 61 objRef []*ObjectRef 62 tableDefs []*TableDef 63 isClusterTable []bool 64 haveConstraint bool 65 updateCol []map[string]int32 // name=updated col.Name value=col position in TableDef.Cols 66 updateKeys []map[string]tree.Expr // This slice index correspond to tableDefs 67 oldColPosMap []map[string]int // origin table values to their position in derived table 68 newColPosMap []map[string]int // insert/update values to their position in derived table 69 nameToIdx map[string]int // Mapping of table full path name to tableDefs index,such as: 'tpch.nation -> 0' 70 idToName map[uint64]string // Mapping of tableId to full path name of table 71 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 72 } 73 74 func getAliasToName(ctx CompilerContext, expr tree.TableExpr, alias string, aliasMap map[string][2]string) { 75 switch t := expr.(type) { 76 case *tree.TableName: 77 dbName := string(t.SchemaName) 78 if dbName == "" { 79 dbName = ctx.DefaultDatabase() 80 } 81 tblName := string(t.ObjectName) 82 if alias != "" { 83 aliasMap[alias] = [2]string{dbName, tblName} 84 } 85 case *tree.AliasedTableExpr: 86 alias := string(t.As.Alias) 87 getAliasToName(ctx, t.Expr, alias, aliasMap) 88 case *tree.JoinTableExpr: 89 getAliasToName(ctx, t.Left, alias, aliasMap) 90 getAliasToName(ctx, t.Right, alias, aliasMap) 91 } 92 } 93 94 func getUpdateTableInfo(ctx CompilerContext, stmt *tree.Update) (*dmlTableInfo, error) { 95 tblInfo, err := getDmlTableInfo(ctx, stmt.Tables, stmt.With, nil) 96 if err != nil { 97 return nil, err 98 } 99 100 //check update field and set updateKeys 101 usedTbl := make(map[string]map[string]tree.Expr) 102 allColumns := make(map[string]map[string]struct{}) 103 for alias, idx := range tblInfo.alias { 104 allColumns[alias] = make(map[string]struct{}) 105 for _, col := range tblInfo.tableDefs[idx].Cols { 106 allColumns[alias][col.Name] = struct{}{} 107 } 108 } 109 110 appendToTbl := func(table, column string, expr tree.Expr) { 111 if _, exists := usedTbl[table]; !exists { 112 usedTbl[table] = make(map[string]tree.Expr) 113 } 114 usedTbl[table][column] = expr 115 } 116 117 for _, updateExpr := range stmt.Exprs { 118 if len(updateExpr.Names) > 1 { 119 return nil, moerr.NewNYI(ctx.GetContext(), "unsupport expr") 120 } 121 parts := updateExpr.Names[0] 122 expr := updateExpr.Expr 123 if parts.NumParts > 1 { 124 colName := parts.Parts[0] 125 tblName := parts.Parts[1] 126 if _, tblExists := tblInfo.alias[tblName]; tblExists { 127 if _, colExists := allColumns[tblName][colName]; colExists { 128 appendToTbl(tblName, colName, expr) 129 } else { 130 return nil, moerr.NewInternalError(ctx.GetContext(), "column '%v' not found in table %s", colName, tblName) 131 } 132 } else { 133 return nil, moerr.NewNoSuchTable(ctx.GetContext(), "", tblName) 134 } 135 } else { 136 colName := parts.Parts[0] 137 tblName := "" 138 found := false 139 for alias, colulmns := range allColumns { 140 if _, colExists := colulmns[colName]; colExists { 141 if tblName != "" { 142 return nil, moerr.NewInternalError(ctx.GetContext(), "Column '%v' in field list is ambiguous", colName) 143 } 144 found = true 145 appendToTbl(alias, colName, expr) 146 } 147 } 148 if !found { 149 return nil, moerr.NewInternalError(ctx.GetContext(), "column '%v' not found in table %s", colName, tblName) 150 } 151 } 152 } 153 154 // remove unused table 155 newTblInfo := &dmlTableInfo{ 156 nameToIdx: make(map[string]int), 157 idToName: make(map[uint64]string), 158 alias: make(map[string]int), 159 } 160 for alias, columns := range usedTbl { 161 idx := tblInfo.alias[alias] 162 tblDef := tblInfo.tableDefs[idx] 163 newTblInfo.objRef = append(newTblInfo.objRef, tblInfo.objRef[idx]) 164 newTblInfo.tableDefs = append(newTblInfo.tableDefs, tblDef) 165 newTblInfo.isClusterTable = append(newTblInfo.isClusterTable, tblInfo.isClusterTable[idx]) 166 newTblInfo.alias[alias] = len(newTblInfo.tableDefs) - 1 167 newTblInfo.updateKeys = append(newTblInfo.updateKeys, columns) 168 169 if !newTblInfo.haveConstraint { 170 if len(tblDef.RefChildTbls) > 0 { 171 newTblInfo.haveConstraint = true 172 } else if len(tblDef.Fkeys) > 0 { 173 newTblInfo.haveConstraint = true 174 } else { 175 for _, indexdef := range tblDef.Indexes { 176 if indexdef.Unique { 177 newTblInfo.haveConstraint = true 178 break 179 } 180 } 181 } 182 } 183 } 184 for idx, ref := range newTblInfo.objRef { 185 key := ref.SchemaName + "." + ref.ObjName 186 newTblInfo.idToName[newTblInfo.tableDefs[idx].TblId] = key 187 newTblInfo.nameToIdx[key] = idx 188 } 189 190 return newTblInfo, nil 191 } 192 193 func setTableExprToDmlTableInfo(ctx CompilerContext, tbl tree.TableExpr, tblInfo *dmlTableInfo, aliasMap map[string][2]string, withMap map[string]struct{}) error { 194 var tblName, dbName, alias string 195 196 if aliasTbl, ok := tbl.(*tree.AliasedTableExpr); ok { 197 alias = string(aliasTbl.As.Alias) 198 tbl = aliasTbl.Expr 199 } 200 201 if jionTbl, ok := tbl.(*tree.JoinTableExpr); ok { 202 err := setTableExprToDmlTableInfo(ctx, jionTbl.Left, tblInfo, aliasMap, withMap) 203 if err != nil { 204 return err 205 } 206 if jionTbl.Right != nil { 207 return setTableExprToDmlTableInfo(ctx, jionTbl.Right, tblInfo, aliasMap, withMap) 208 } 209 return nil 210 } 211 212 if baseTbl, ok := tbl.(*tree.TableName); ok { 213 tblName = string(baseTbl.ObjectName) 214 dbName = string(baseTbl.SchemaName) 215 } 216 217 if _, exist := withMap[tblName]; exist { 218 return nil 219 } 220 221 if aliasNames, exist := aliasMap[tblName]; exist { 222 dbName = aliasNames[0] 223 tblName = aliasNames[1] 224 } 225 226 if tblName == "" { 227 return nil 228 } 229 230 if dbName == "" { 231 dbName = ctx.DefaultDatabase() 232 } 233 234 _, tableDef := ctx.Resolve(dbName, tblName) 235 if tableDef == nil { 236 return moerr.NewNoSuchTable(ctx.GetContext(), dbName, tblName) 237 } 238 if tableDef.TableType == catalog.SystemExternalRel { 239 return moerr.NewInvalidInput(ctx.GetContext(), "cannot insert/update/delete from external table") 240 } else if tableDef.TableType == catalog.SystemViewRel { 241 return moerr.NewInvalidInput(ctx.GetContext(), "cannot insert/update/delete from view") 242 } 243 244 isClusterTable := util.TableIsClusterTable(tableDef.GetTableType()) 245 if isClusterTable && ctx.GetAccountId() != catalog.System_Account { 246 return moerr.NewInternalError(ctx.GetContext(), "only the sys account can insert/update/delete the cluster table") 247 } 248 249 if util.TableIsClusterTable(tableDef.GetTableType()) && ctx.GetAccountId() != catalog.System_Account { 250 return moerr.NewInternalError(ctx.GetContext(), "only the sys account can insert/update/delete the cluster table %s", tableDef.GetName()) 251 } 252 253 if !tblInfo.haveConstraint { 254 if len(tableDef.RefChildTbls) > 0 { 255 tblInfo.haveConstraint = true 256 } else if len(tableDef.Fkeys) > 0 { 257 tblInfo.haveConstraint = true 258 } else { 259 for _, indexdef := range tableDef.Indexes { 260 if indexdef.Unique { 261 tblInfo.haveConstraint = true 262 break 263 } 264 } 265 } 266 } 267 268 nowIdx := len(tblInfo.tableDefs) 269 tblInfo.isClusterTable = append(tblInfo.isClusterTable, isClusterTable) 270 tblInfo.objRef = append(tblInfo.objRef, &ObjectRef{ 271 Obj: int64(tableDef.TblId), 272 SchemaName: dbName, 273 ObjName: tblName, 274 }) 275 tblInfo.tableDefs = append(tblInfo.tableDefs, tableDef) 276 key := dbName + "." + tblName 277 tblInfo.nameToIdx[key] = nowIdx 278 tblInfo.idToName[tableDef.TblId] = key 279 if alias == "" { 280 alias = tblName 281 } 282 tblInfo.alias[alias] = nowIdx 283 284 return nil 285 } 286 287 func getDmlTableInfo(ctx CompilerContext, tableExprs tree.TableExprs, with *tree.With, aliasMap map[string][2]string) (*dmlTableInfo, error) { 288 tblInfo := &dmlTableInfo{ 289 nameToIdx: make(map[string]int), 290 idToName: make(map[uint64]string), 291 alias: make(map[string]int), 292 } 293 294 cteMap := make(map[string]struct{}) 295 if with != nil { 296 for _, cte := range with.CTEs { 297 cteMap[string(cte.Name.Alias)] = struct{}{} 298 } 299 } 300 301 for _, tbl := range tableExprs { 302 err := setTableExprToDmlTableInfo(ctx, tbl, tblInfo, aliasMap, cteMap) 303 if err != nil { 304 return nil, err 305 } 306 } 307 308 return tblInfo, nil 309 } 310 311 func updateToSelect(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Update, tableInfo *dmlTableInfo, haveConstraint bool) (int32, error) { 312 fromTables := &tree.From{ 313 Tables: stmt.Tables, 314 } 315 selectList := make([]tree.SelectExpr, len(tableInfo.tableDefs)) 316 317 // append table.* to project list 318 columnsSize := 0 319 for alias, i := range tableInfo.alias { 320 e, _ := tree.NewUnresolvedNameWithStar(builder.GetContext(), alias) 321 columnsSize += len(tableInfo.tableDefs[i].Cols) 322 selectList[i] = tree.SelectExpr{ 323 Expr: e, 324 } 325 } 326 327 // append [update expr] to project list 328 counter := 0 329 updateColsOffset := make([]map[string]int, len(tableInfo.updateKeys)) 330 for idx, tbUpdateMap := range tableInfo.updateKeys { 331 updateColsOffset[idx] = make(map[string]int) 332 for colName, updateCol := range tbUpdateMap { 333 valuePos := columnsSize + counter 334 // Add update expression after select list 335 selectList = append(selectList, tree.SelectExpr{ 336 Expr: updateCol, 337 }) 338 updateColsOffset[idx][colName] = valuePos 339 counter++ 340 } 341 } 342 343 // origin table values to their position in dev derived table 344 oldColPosMap := make([]map[string]int, len(tableInfo.tableDefs)) 345 // insert/update values to their position in derived table 346 newColPosMap := make([]map[string]int, len(tableInfo.tableDefs)) 347 projectSeq := 0 348 updateCol := make([]map[string]int32, len(tableInfo.updateKeys)) 349 for idx, tableDef := range tableInfo.tableDefs { 350 //append update 351 oldColPosMap[idx] = make(map[string]int) 352 newColPosMap[idx] = make(map[string]int) 353 updateCol[idx] = make(map[string]int32) 354 for j, coldef := range tableDef.Cols { 355 oldColPosMap[idx][coldef.Name] = projectSeq + j 356 if pos, ok := updateColsOffset[idx][coldef.Name]; ok { 357 newColPosMap[idx][coldef.Name] = pos 358 updateCol[idx][coldef.Name] = int32(j) 359 } else { 360 newColPosMap[idx][coldef.Name] = projectSeq + j 361 } 362 } 363 projectSeq += len(tableDef.Cols) 364 } 365 tableInfo.oldColPosMap = oldColPosMap 366 tableInfo.newColPosMap = newColPosMap 367 tableInfo.updateCol = updateCol 368 369 selectAst := &tree.Select{ 370 Select: &tree.SelectClause{ 371 Distinct: false, 372 Exprs: selectList, 373 From: fromTables, 374 Where: stmt.Where, 375 }, 376 OrderBy: stmt.OrderBy, 377 Limit: stmt.Limit, 378 With: stmt.With, 379 } 380 //ftCtx := tree.NewFmtCtx(dialect.MYSQL) 381 //selectAst.Format(ftCtx) 382 //sql := ftCtx.String() 383 //fmt.Print(sql) 384 return builder.buildSelect(selectAst, bindCtx, false) 385 } 386 387 func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Insert, info *dmlSelectInfo) error { 388 var err error 389 var updateColumns []string 390 tableDef := info.tblInfo.tableDefs[0] 391 syntaxHasColumnNames := false 392 isClusterTable := info.tblInfo.isClusterTable[0] 393 colToIdx := make(map[string]int) 394 oldColPosMap := make(map[string]int) 395 for i, col := range tableDef.Cols { 396 colToIdx[col.Name] = i 397 oldColPosMap[col.Name] = i 398 } 399 info.tblInfo.oldColPosMap = append(info.tblInfo.oldColPosMap, oldColPosMap) 400 info.tblInfo.newColPosMap = append(info.tblInfo.newColPosMap, oldColPosMap) 401 402 if stmt.Columns == nil { 403 if isClusterTable { 404 for _, col := range tableDef.Cols { 405 if !util.IsClusterTableAttribute(col.Name) { 406 updateColumns = append(updateColumns, col.Name) 407 } 408 } 409 } else { 410 for _, col := range tableDef.Cols { 411 updateColumns = append(updateColumns, col.Name) 412 } 413 } 414 } else { 415 syntaxHasColumnNames = true 416 for _, column := range stmt.Columns { 417 colName := string(column) 418 if isClusterTable && util.IsClusterTableAttribute(colName) { 419 return moerr.NewInvalidInput(builder.GetContext(), "do not specify the attribute %s for the cluster table", util.GetClusterTableAttributeName()) 420 } 421 if _, exists := colToIdx[string(column)]; !exists { 422 return moerr.NewInvalidInput(builder.GetContext(), "insert value into unknown column '%s'", colName) 423 } 424 updateColumns = append(updateColumns, colName) 425 } 426 } 427 428 var astSlt *tree.Select 429 switch slt := stmt.Rows.Select.(type) { 430 // rewrite 'insert into tbl values (1,1)' to 'insert into tbl select * from (values row(1,1))' 431 case *tree.ValuesClause: 432 isAllDefault := false 433 if slt.Rows[0] == nil { 434 isAllDefault = true 435 } 436 if isAllDefault { 437 for j, row := range slt.Rows { 438 if row != nil { 439 return moerr.NewInternalError(builder.GetContext(), fmt.Sprintf("Column count doesn't match value count at row '%v'", j)) 440 } 441 } 442 } else { 443 colCount := len(updateColumns) 444 for j, row := range slt.Rows { 445 if len(row) != colCount { 446 return moerr.NewInternalError(builder.GetContext(), fmt.Sprintf("Column count doesn't match value count at row '%v'", j)) 447 } 448 } 449 } 450 451 //example1:insert into a values (); 452 //but it does not work at the case: 453 //insert into a(a) values (); insert into a values (0),(); 454 if isAllDefault && syntaxHasColumnNames { 455 return moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns") 456 } 457 458 err = buildValueScan(isAllDefault, info, builder, bindCtx, tableDef, slt, updateColumns, colToIdx) 459 if err != nil { 460 return err 461 } 462 463 case *tree.SelectClause: 464 astSlt = stmt.Rows 465 466 subCtx := NewBindContext(builder, bindCtx) 467 info.rootId, err = builder.buildSelect(astSlt, subCtx, false) 468 if err != nil { 469 return err 470 } 471 472 case *tree.ParenSelect: 473 astSlt = slt.Select 474 475 subCtx := NewBindContext(builder, bindCtx) 476 info.rootId, err = builder.buildSelect(astSlt, subCtx, false) 477 if err != nil { 478 return err 479 } 480 481 default: 482 return moerr.NewInvalidInput(builder.GetContext(), "insert has unknown select statement") 483 } 484 485 err = builder.addBinding(info.rootId, tree.AliasClause{ 486 Alias: derivedTableName, 487 }, bindCtx) 488 if err != nil { 489 return err 490 } 491 492 lastNode := builder.qry.Nodes[info.rootId] 493 if len(updateColumns) != len(lastNode.ProjectList) { 494 return moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns") 495 } 496 497 tag := builder.qry.Nodes[info.rootId].BindingTags[0] 498 info.derivedTableId = info.rootId 499 oldProject := append([]*Expr{}, lastNode.ProjectList...) 500 501 insertColToExpr := make(map[string]*Expr) 502 for i, column := range updateColumns { 503 colIdx := colToIdx[column] 504 projExpr := &plan.Expr{ 505 Typ: oldProject[i].Typ, 506 Expr: &plan.Expr_Col{ 507 Col: &plan.ColRef{ 508 RelPos: tag, 509 ColPos: int32(i), 510 }, 511 }, 512 } 513 projExpr, err = forceCastExpr(builder.GetContext(), projExpr, tableDef.Cols[colIdx].Typ) 514 if err != nil { 515 return err 516 } 517 insertColToExpr[column] = projExpr 518 } 519 520 // have tables : t1(a default 0, b int, pk(a,b)) , t2(j int,k int) 521 // rewrite 'insert into t1 select * from t2' to 522 // select 'select _t.j, _t.k from (select * from t2) _t 523 // -------- 524 // rewrite 'insert into t1(b) values (1)' to 525 // select 'select 0, _t.column_0 from (select * from values (1)) _t 526 projectList := make([]*Expr, 0, len(tableDef.Cols)) 527 for _, col := range tableDef.Cols { 528 if oldExpr, exists := insertColToExpr[col.Name]; exists { 529 projectList = append(projectList, oldExpr) 530 } else { 531 defExpr, err := getDefaultExpr(builder.GetContext(), col) 532 if err != nil { 533 return err 534 } 535 projectList = append(projectList, defExpr) 536 } 537 } 538 539 // append ProjectNode 540 projectCtx := NewBindContext(builder, bindCtx) 541 lastTag := builder.genNewTag() 542 info.rootId = builder.appendNode(&plan.Node{ 543 NodeType: plan.Node_PROJECT, 544 ProjectList: projectList, 545 Children: []int32{info.rootId}, 546 BindingTags: []int32{lastTag}, 547 }, projectCtx) 548 549 info.projectList = make([]*Expr, len(projectList)) 550 info.derivedTableId = info.rootId 551 for i, e := range projectList { 552 info.projectList[i] = &plan.Expr{ 553 Typ: e.Typ, 554 Expr: &plan.Expr_Col{ 555 Col: &plan.ColRef{ 556 RelPos: lastTag, 557 ColPos: int32(i), 558 }, 559 }, 560 } 561 } 562 info.idx = int32(len(info.projectList)) 563 564 return nil 565 } 566 567 func deleteToSelect(builder *QueryBuilder, bindCtx *BindContext, node *tree.Delete, haveConstraint bool) (int32, error) { 568 var selectList []tree.SelectExpr 569 fromTables := &tree.From{} 570 571 getResolveExpr := func(tblName string) tree.SelectExpr { 572 var ret *tree.UnresolvedName 573 if haveConstraint { 574 ret, _ = tree.NewUnresolvedNameWithStar(builder.GetContext(), tblName) 575 } else { 576 ret, _ = tree.NewUnresolvedName(builder.GetContext(), tblName, catalog.Row_ID) 577 } 578 return tree.SelectExpr{ 579 Expr: ret, 580 } 581 } 582 583 for _, tbl := range node.Tables { 584 if aliasTbl, ok := tbl.(*tree.AliasedTableExpr); ok { 585 alias := string(aliasTbl.As.Alias) 586 if alias != "" { 587 selectList = append(selectList, getResolveExpr(alias)) 588 } else { 589 astTbl := aliasTbl.Expr.(*tree.TableName) 590 selectList = append(selectList, getResolveExpr(string(astTbl.ObjectName))) 591 } 592 } else if astTbl, ok := tbl.(*tree.TableName); ok { 593 selectList = append(selectList, getResolveExpr(string(astTbl.ObjectName))) 594 } 595 } 596 597 if node.TableRefs != nil { 598 fromTables.Tables = node.TableRefs 599 } else { 600 fromTables.Tables = node.Tables 601 } 602 603 astSelect := &tree.Select{ 604 Select: &tree.SelectClause{ 605 Distinct: false, 606 Exprs: selectList, 607 From: fromTables, 608 Where: node.Where, 609 }, 610 OrderBy: node.OrderBy, 611 Limit: node.Limit, 612 With: node.With, 613 } 614 // ftCtx := tree.NewFmtCtx(dialectType) 615 // astSelect.Format(ftCtx) 616 // sql := ftCtx.String() 617 // fmt.Print(sql) 618 619 return builder.buildSelect(astSelect, bindCtx, false) 620 } 621 622 func initDeleteStmt(builder *QueryBuilder, bindCtx *BindContext, info *dmlSelectInfo, stmt *tree.Delete) error { 623 var err error 624 subCtx := NewBindContext(builder, bindCtx) 625 info.rootId, err = deleteToSelect(builder, subCtx, stmt, true) 626 if err != nil { 627 return err 628 } 629 630 err = builder.addBinding(info.rootId, tree.AliasClause{ 631 Alias: derivedTableName, 632 }, bindCtx) 633 if err != nil { 634 return err 635 } 636 637 lastNode := builder.qry.Nodes[info.rootId] 638 tag := builder.qry.Nodes[info.rootId].BindingTags[0] 639 info.derivedTableId = info.rootId 640 641 // origin table values to their position 642 oldColPosMap := make([]map[string]int, len(info.tblInfo.tableDefs)) 643 projectSeq := 0 644 for idx, tableDef := range info.tblInfo.tableDefs { 645 oldColPosMap[idx] = make(map[string]int) 646 for j, coldef := range tableDef.Cols { 647 pos := projectSeq + j 648 oldColPosMap[idx][coldef.Name] = pos 649 if coldef.Name == catalog.Row_ID { 650 info.projectList = append(info.projectList, &plan.Expr{ 651 Typ: coldef.Typ, 652 Expr: &plan.Expr_Col{ 653 Col: &plan.ColRef{ 654 RelPos: tag, 655 ColPos: int32(pos), 656 }, 657 }, 658 }) 659 } 660 } 661 projectSeq += len(tableDef.Cols) 662 } 663 info.tblInfo.oldColPosMap = oldColPosMap 664 info.tblInfo.newColPosMap = oldColPosMap //we donot need this field in delete statement 665 666 for idx, expr := range lastNode.ProjectList { 667 if expr.Typ.Id == int32(types.T_Rowid) { 668 info.projectList = append(info.projectList, &plan.Expr{ 669 Typ: expr.Typ, 670 Expr: &plan.Expr_Col{ 671 Col: &plan.ColRef{ 672 RelPos: tag, 673 ColPos: int32(idx), 674 }, 675 }, 676 }) 677 } 678 } 679 info.idx = int32(len(info.projectList)) 680 return nil 681 } 682 683 func checkNotNull(ctx context.Context, expr *Expr, tableDef *TableDef, col *ColDef) error { 684 isConstantNull := false 685 if ef, ok := expr.Expr.(*plan.Expr_C); ok { 686 isConstantNull = ef.C.Isnull 687 } 688 if !isConstantNull { 689 return nil 690 } 691 692 if col.NotNull { 693 return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", col.Name)) 694 } 695 696 if (col.Primary && !col.Typ.AutoIncr) || 697 (col.Default != nil && !col.Default.NullAbility) { 698 return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", col.Name)) 699 } 700 701 if tableDef.CompositePkey != nil { 702 names := util.SplitCompositePrimaryKeyColumnName(tableDef.CompositePkey.Name) 703 for _, name := range names { 704 if name == col.Name { 705 return moerr.NewConstraintViolation(ctx, fmt.Sprintf("Column '%s' cannot be null", name)) 706 } 707 } 708 } 709 710 return nil 711 } 712 713 func forceCastExpr(ctx context.Context, expr *Expr, targetType *Type) (*Expr, error) { 714 t1, t2 := makeTypeByPlan2Expr(expr), makeTypeByPlan2Type(targetType) 715 if t1.Oid == t2.Oid && t1.Width == t2.Width && t1.Precision == t2.Precision && t1.Size == t2.Size && t1.Scale == t2.Scale { 716 return expr, nil 717 } 718 719 targetType.NotNullable = expr.Typ.NotNullable 720 id, _, _, err := function.GetFunctionByName(ctx, "cast", []types.Type{t1, t2}) 721 if err != nil { 722 return nil, err 723 } 724 t := &plan.Expr{ 725 Typ: targetType, 726 Expr: &plan.Expr_T{ 727 T: &plan.TargetType{ 728 Typ: targetType, 729 }, 730 }, 731 } 732 return &plan.Expr{ 733 Expr: &plan.Expr_F{ 734 F: &plan.Function{ 735 Func: &ObjectRef{Obj: id, ObjName: "cast"}, 736 Args: []*Expr{expr, t}, 737 }, 738 }, 739 Typ: targetType, 740 }, nil 741 } 742 743 func initUpdateStmt(builder *QueryBuilder, bindCtx *BindContext, info *dmlSelectInfo, stmt *tree.Update) error { 744 var err error 745 subCtx := NewBindContext(builder, bindCtx) 746 747 info.rootId, err = updateToSelect(builder, subCtx, stmt, info.tblInfo, true) 748 if err != nil { 749 return err 750 } 751 752 err = builder.addBinding(info.rootId, tree.AliasClause{ 753 Alias: derivedTableName, 754 }, bindCtx) 755 if err != nil { 756 return err 757 } 758 759 lastNode := builder.qry.Nodes[info.rootId] 760 tag := lastNode.BindingTags[0] 761 info.derivedTableId = info.rootId 762 763 idx := 0 764 for i, tableDef := range info.tblInfo.tableDefs { 765 updateKeysMap := info.tblInfo.updateKeys[i] 766 newColPosMap := info.tblInfo.newColPosMap[i] 767 nameToIdx := make(map[string]int32) 768 for j, coldef := range tableDef.Cols { 769 nameToIdx[coldef.Name] = int32(j) 770 } 771 772 for _, coldef := range tableDef.Cols { 773 if _, ok := updateKeysMap[coldef.Name]; ok { 774 pos := newColPosMap[coldef.Name] 775 posExpr := lastNode.ProjectList[pos] 776 if posExpr.Typ == nil { // set col = default 777 lastNode.ProjectList[pos], err = getDefaultExpr(builder.GetContext(), coldef) 778 if err != nil { 779 return err 780 } 781 posExpr = lastNode.ProjectList[pos] 782 } 783 err = checkNotNull(builder.GetContext(), posExpr, tableDef, coldef) 784 if err != nil { 785 return err 786 } 787 lastNode.ProjectList[pos], err = forceCastExpr(builder.GetContext(), posExpr, coldef.Typ) 788 if err != nil { 789 return err 790 } 791 projExpr := &plan.Expr{ 792 Typ: coldef.Typ, 793 Expr: &plan.Expr_Col{ 794 Col: &plan.ColRef{ 795 RelPos: tag, 796 ColPos: int32(pos), 797 }, 798 }, 799 } 800 info.projectList = append(info.projectList, projExpr) 801 802 } else { 803 if coldef.OnUpdate != nil && coldef.OnUpdate.Expr != nil { 804 lastNode.ProjectList[idx] = coldef.OnUpdate.Expr 805 } 806 807 lastNode.ProjectList[idx], err = forceCastExpr(builder.GetContext(), lastNode.ProjectList[idx], coldef.Typ) 808 if err != nil { 809 return err 810 } 811 812 info.projectList = append(info.projectList, &plan.Expr{ 813 Typ: coldef.Typ, 814 Expr: &plan.Expr_Col{ 815 Col: &plan.ColRef{ 816 RelPos: tag, 817 ColPos: int32(idx), 818 }, 819 }, 820 }) 821 } 822 idx++ 823 } 824 } 825 info.idx = int32(len(info.projectList)) 826 return nil 827 } 828 829 func rewriteDmlSelectInfo(builder *QueryBuilder, bindCtx *BindContext, info *dmlSelectInfo, tableDef *TableDef, baseNodeId int32, rewriteIdx int) error { 830 // posMap := make(map[string]int32) 831 typMap := make(map[string]*plan.Type) 832 id2name := make(map[uint64]string) 833 834 //use origin query as left, we need add prefix pos 835 var oldColPosMap map[string]int 836 var newColPosMap map[string]int 837 if rewriteIdx > -1 { 838 oldColPosMap = info.tblInfo.oldColPosMap[rewriteIdx] 839 newColPosMap = info.tblInfo.newColPosMap[rewriteIdx] 840 for _, col := range tableDef.Cols { 841 typMap[col.Name] = col.Typ 842 id2name[col.ColId] = col.Name 843 } 844 845 } else { 846 // unsupport deep level, no test 847 oldColPosMap = make(map[string]int) 848 newColPosMap = make(map[string]int) 849 for idx, col := range tableDef.Cols { 850 oldColPosMap[col.Name] = idx 851 newColPosMap[col.Name] = idx 852 typMap[col.Name] = col.Typ 853 id2name[col.ColId] = col.Name 854 } 855 } 856 857 // rewrite index, to get rows of unique table to delete 858 if info.typ != "insert" { 859 if tableDef.Indexes != nil { 860 for _, indexdef := range tableDef.Indexes { 861 if indexdef.Unique { 862 idxRef := &plan.ObjectRef{ 863 SchemaName: builder.compCtx.DefaultDatabase(), 864 ObjName: indexdef.IndexTableName, 865 } 866 867 // append table_scan node 868 joinCtx := NewBindContext(builder, bindCtx) 869 870 rightCtx := NewBindContext(builder, joinCtx) 871 astTblName := tree.NewTableName(tree.Identifier(indexdef.IndexTableName), tree.ObjectNamePrefix{}) 872 rightId, err := builder.buildTable(astTblName, rightCtx) 873 if err != nil { 874 return err 875 } 876 rightTag := builder.qry.Nodes[rightId].BindingTags[0] 877 baseTag := builder.qry.Nodes[baseNodeId].BindingTags[0] 878 rightTableDef := builder.qry.Nodes[rightId].TableDef 879 rightRowIdPos := int32(len(rightTableDef.Cols)) - 1 880 rightIdxPos := int32(0) 881 882 // append projection 883 info.projectList = append(info.projectList, &plan.Expr{ 884 Typ: rightTableDef.Cols[rightRowIdPos].Typ, 885 Expr: &plan.Expr_Col{ 886 Col: &plan.ColRef{ 887 RelPos: rightTag, 888 ColPos: rightRowIdPos, 889 }, 890 }, 891 }) 892 893 rightExpr := &plan.Expr{ 894 Typ: rightTableDef.Cols[rightIdxPos].Typ, 895 Expr: &plan.Expr_Col{ 896 Col: &plan.ColRef{ 897 RelPos: rightTag, 898 ColPos: rightIdxPos, 899 }, 900 }, 901 } 902 903 // append join node 904 var joinConds []*Expr 905 var leftExpr *Expr 906 partsLength := len(indexdef.Parts) 907 if partsLength == 1 { 908 orginIndexColumnName := indexdef.Parts[0] 909 typ := typMap[orginIndexColumnName] 910 leftExpr = &Expr{ 911 Typ: typ, 912 Expr: &plan.Expr_Col{ 913 Col: &plan.ColRef{ 914 RelPos: baseTag, 915 ColPos: int32(oldColPosMap[orginIndexColumnName]), 916 }, 917 }, 918 } 919 } else { 920 args := make([]*Expr, partsLength) 921 for i, column := range indexdef.Parts { 922 typ := typMap[column] 923 args[i] = &plan.Expr{ 924 Typ: typ, 925 Expr: &plan.Expr_Col{ 926 Col: &plan.ColRef{ 927 RelPos: baseTag, 928 ColPos: int32(oldColPosMap[column]), 929 }, 930 }, 931 } 932 } 933 leftExpr, err = bindFuncExprImplByPlanExpr(builder.GetContext(), "serial", args) 934 if err != nil { 935 return err 936 } 937 } 938 939 condExpr, err := bindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{leftExpr, rightExpr}) 940 if err != nil { 941 return err 942 } 943 joinConds = []*Expr{condExpr} 944 945 leftCtx := builder.ctxByNode[info.rootId] 946 err = joinCtx.mergeContexts(builder.GetContext(), leftCtx, rightCtx) 947 if err != nil { 948 return err 949 } 950 newRootId := builder.appendNode(&plan.Node{ 951 NodeType: plan.Node_JOIN, 952 Children: []int32{info.rootId, rightId}, 953 JoinType: plan.Node_LEFT, 954 OnList: joinConds, 955 }, joinCtx) 956 bindCtx.binder = NewTableBinder(builder, bindCtx) 957 info.rootId = newRootId 958 info.onIdxTbl = append(info.onIdxTbl, idxRef) 959 info.onIdx = append(info.onIdx, info.idx) 960 info.idx = info.idx + 1 961 } 962 } 963 } 964 } 965 966 // check child table 967 if info.typ != "insert" { 968 for _, tableId := range tableDef.RefChildTbls { 969 if _, existInDelTable := info.tblInfo.idToName[tableId]; existInDelTable { 970 // delete parent_tbl, child_tbl from parent_tbl join child_tbl xxxxxx 971 // we will skip child_tbl here. 972 continue 973 } 974 975 _, childTableDef := builder.compCtx.ResolveById(tableId) 976 childPosMap := make(map[string]int32) 977 childTypMap := make(map[string]*plan.Type) 978 childId2name := make(map[uint64]string) 979 for idx, col := range childTableDef.Cols { 980 childPosMap[col.Name] = int32(idx) 981 childTypMap[col.Name] = col.Typ 982 childId2name[col.ColId] = col.Name 983 } 984 985 objRef := &plan.ObjectRef{ 986 Obj: int64(childTableDef.TblId), 987 SchemaName: builder.compCtx.DefaultDatabase(), 988 ObjName: childTableDef.Name, 989 } 990 991 for _, fk := range childTableDef.Fkeys { 992 if fk.ForeignTbl == tableDef.TblId { 993 // in update statement. only add left join logic when update the column in foreign key 994 if info.typ == "update" { 995 updateRefColumn := false 996 for _, colId := range fk.ForeignCols { 997 updateName := id2name[colId] 998 if _, ok := info.tblInfo.updateKeys[rewriteIdx][updateName]; ok { 999 updateRefColumn = true 1000 break 1001 } 1002 } 1003 if !updateRefColumn { 1004 continue 1005 } 1006 } 1007 1008 // append table scan node 1009 joinCtx := NewBindContext(builder, bindCtx) 1010 rightCtx := NewBindContext(builder, joinCtx) 1011 astTblName := tree.NewTableName(tree.Identifier(childTableDef.Name), tree.ObjectNamePrefix{}) 1012 rightId, err := builder.buildTable(astTblName, rightCtx) 1013 if err != nil { 1014 return err 1015 } 1016 rightTag := builder.qry.Nodes[rightId].BindingTags[0] 1017 baseNodeTag := builder.qry.Nodes[baseNodeId].BindingTags[0] 1018 // needRecursionCall := false 1019 1020 // build join conds 1021 joinConds := make([]*Expr, len(fk.Cols)) 1022 for i, colId := range fk.Cols { 1023 for _, col := range childTableDef.Cols { 1024 if col.ColId == colId { 1025 childColumnName := col.Name 1026 originColumnName := id2name[fk.ForeignCols[i]] 1027 1028 leftExpr := &Expr{ 1029 Typ: typMap[originColumnName], 1030 Expr: &plan.Expr_Col{ 1031 Col: &plan.ColRef{ 1032 RelPos: baseNodeTag, 1033 ColPos: int32(oldColPosMap[originColumnName]), 1034 }, 1035 }, 1036 } 1037 rightExpr := &plan.Expr{ 1038 Typ: childTypMap[childColumnName], 1039 Expr: &plan.Expr_Col{ 1040 Col: &plan.ColRef{ 1041 RelPos: rightTag, 1042 ColPos: childPosMap[childColumnName], 1043 }, 1044 }, 1045 } 1046 condExpr, err := bindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{leftExpr, rightExpr}) 1047 if err != nil { 1048 return err 1049 } 1050 joinConds[i] = condExpr 1051 break 1052 } 1053 } 1054 } 1055 1056 // append project 1057 var refAction plan.ForeignKeyDef_RefAction 1058 if info.typ == "update" { 1059 refAction = fk.OnUpdate 1060 } else { 1061 refAction = fk.OnDelete 1062 } 1063 1064 switch refAction { 1065 case plan.ForeignKeyDef_NO_ACTION, plan.ForeignKeyDef_RESTRICT, plan.ForeignKeyDef_SET_DEFAULT: 1066 info.projectList = append(info.projectList, &plan.Expr{ 1067 Typ: childTypMap[catalog.Row_ID], 1068 Expr: &plan.Expr_Col{ 1069 Col: &plan.ColRef{ 1070 RelPos: rightTag, 1071 ColPos: childPosMap[catalog.Row_ID], 1072 }, 1073 }, 1074 }) 1075 info.onRestrict = append(info.onRestrict, info.idx) 1076 info.idx = info.idx + 1 1077 info.onRestrictTbl = append(info.onRestrictTbl, objRef) 1078 1079 case plan.ForeignKeyDef_CASCADE: 1080 // for update ,we need to reset column's value of child table, just like set null 1081 updateCol := make(map[string]int32) 1082 if info.typ == "update" { 1083 fkIdMap := make(map[uint64]uint64) 1084 for j, colId := range fk.Cols { 1085 fkIdMap[colId] = fk.ForeignCols[j] 1086 } 1087 1088 var setIdxs []int64 1089 for j, col := range childTableDef.Cols { 1090 if pIdx, ok := fkIdMap[col.ColId]; ok { 1091 originName := id2name[pIdx] 1092 info.projectList = append(info.projectList, &plan.Expr{ 1093 Typ: col.Typ, 1094 Expr: &plan.Expr_Col{ 1095 Col: &plan.ColRef{ 1096 RelPos: baseNodeTag, 1097 ColPos: int32(newColPosMap[originName]), 1098 }, 1099 }, 1100 }) 1101 updateCol[col.Name] = int32(j) 1102 } else { 1103 info.projectList = append(info.projectList, &plan.Expr{ 1104 Typ: col.Typ, 1105 Expr: &plan.Expr_Col{ 1106 Col: &plan.ColRef{ 1107 RelPos: rightTag, 1108 ColPos: int32(j), 1109 }, 1110 }, 1111 }) 1112 } 1113 setIdxs = append(setIdxs, int64(info.idx)) 1114 info.idx = info.idx + 1 1115 } 1116 info.onCascade = append(info.onCascade, setIdxs) 1117 info.onCascadeRef = append(info.onCascadeRef, objRef) 1118 info.onCascadeTableDef = append(info.onCascadeTableDef, childTableDef) 1119 info.onCascadeUpdateCol = append(info.onCascadeUpdateCol, updateCol) 1120 } else { 1121 // for delete, we only get row_id and delete the rows 1122 info.projectList = append(info.projectList, &plan.Expr{ 1123 Typ: childTypMap[catalog.Row_ID], 1124 Expr: &plan.Expr_Col{ 1125 Col: &plan.ColRef{ 1126 RelPos: rightTag, 1127 ColPos: childPosMap[catalog.Row_ID], 1128 }, 1129 }, 1130 }) 1131 info.onCascade = append(info.onCascade, []int64{int64(info.idx)}) 1132 info.idx = info.idx + 1 1133 info.onCascadeRef = append(info.onCascadeRef, objRef) 1134 info.onCascadeUpdateCol = append(info.onCascadeUpdateCol, updateCol) 1135 } 1136 1137 // needRecursionCall = true 1138 1139 case plan.ForeignKeyDef_SET_NULL: 1140 updateCol := make(map[string]int32) 1141 fkIdMap := make(map[uint64]struct{}) 1142 for _, colId := range fk.Cols { 1143 fkIdMap[colId] = struct{}{} 1144 } 1145 var setIdxs []int64 1146 for j, col := range childTableDef.Cols { 1147 if _, ok := fkIdMap[col.ColId]; ok { 1148 info.projectList = append(info.projectList, &plan.Expr{ 1149 Typ: col.Typ, 1150 Expr: &plan.Expr_C{ 1151 C: &Const{ 1152 Isnull: true, 1153 }, 1154 }, 1155 }) 1156 updateCol[col.Name] = int32(j) 1157 } else { 1158 info.projectList = append(info.projectList, &plan.Expr{ 1159 Typ: col.Typ, 1160 Expr: &plan.Expr_Col{ 1161 Col: &plan.ColRef{ 1162 RelPos: rightTag, 1163 ColPos: int32(j), 1164 }, 1165 }, 1166 }) 1167 } 1168 setIdxs = append(setIdxs, int64(info.idx)) 1169 info.idx = info.idx + 1 1170 } 1171 info.onSet = append(info.onSet, setIdxs) 1172 info.onSetRef = append(info.onSetRef, objRef) 1173 info.onSetTableDef = append(info.onSetTableDef, childTableDef) 1174 info.onSetUpdateCol = append(info.onSetUpdateCol, updateCol) 1175 // needRecursionCall = true 1176 } 1177 1178 // append join node 1179 leftCtx := builder.ctxByNode[info.rootId] 1180 err = joinCtx.mergeContexts(builder.GetContext(), leftCtx, rightCtx) 1181 if err != nil { 1182 return err 1183 } 1184 newRootId := builder.appendNode(&plan.Node{ 1185 NodeType: plan.Node_JOIN, 1186 Children: []int32{info.rootId, rightId}, 1187 JoinType: plan.Node_LEFT, 1188 OnList: joinConds, 1189 }, joinCtx) 1190 bindCtx.binder = NewTableBinder(builder, bindCtx) 1191 info.rootId = newRootId 1192 1193 // if needRecursionCall { 1194 1195 // err := rewriteDeleteSelectInfo(builder, bindCtx, info, childTableDef, info.rootId) 1196 // if err != nil { 1197 // return err 1198 // } 1199 // } 1200 } 1201 } 1202 } 1203 } 1204 1205 // check parent table 1206 if info.typ != "delete" { 1207 parentIdx := make(map[string]int32) 1208 1209 for _, fk := range tableDef.Fkeys { 1210 // in update statement. only add left join logic when update the column in foreign key 1211 if info.typ == "update" { 1212 updateRefColumn := false 1213 for _, colId := range fk.Cols { 1214 updateName := id2name[colId] 1215 if _, ok := info.tblInfo.updateKeys[rewriteIdx][updateName]; ok { 1216 updateRefColumn = true 1217 break 1218 } 1219 } 1220 if !updateRefColumn { 1221 continue 1222 } 1223 } 1224 1225 // insert statement, we will alsways check parent ref 1226 for _, colId := range fk.Cols { 1227 updateName := id2name[colId] 1228 parentIdx[updateName] = info.idx 1229 } 1230 1231 _, parentTableDef := builder.compCtx.ResolveById(fk.ForeignTbl) 1232 parentPosMap := make(map[string]int32) 1233 parentTypMap := make(map[string]*plan.Type) 1234 parentId2name := make(map[uint64]string) 1235 for idx, col := range parentTableDef.Cols { 1236 parentPosMap[col.Name] = int32(idx) 1237 parentTypMap[col.Name] = col.Typ 1238 parentId2name[col.ColId] = col.Name 1239 } 1240 1241 // append table scan node 1242 joinCtx := NewBindContext(builder, bindCtx) 1243 1244 rightCtx := NewBindContext(builder, joinCtx) 1245 astTblName := tree.NewTableName(tree.Identifier(parentTableDef.Name), tree.ObjectNamePrefix{}) 1246 rightId, err := builder.buildTable(astTblName, rightCtx) 1247 if err != nil { 1248 return err 1249 } 1250 rightTag := builder.qry.Nodes[rightId].BindingTags[0] 1251 baseNodeTag := builder.qry.Nodes[baseNodeId].BindingTags[0] 1252 // needRecursionCall := false 1253 1254 // build join conds 1255 joinConds := make([]*Expr, len(fk.Cols)) 1256 for i, colId := range fk.ForeignCols { 1257 for _, col := range parentTableDef.Cols { 1258 if col.ColId == colId { 1259 parentColumnName := col.Name 1260 childColumnName := id2name[fk.Cols[i]] 1261 1262 leftExpr := &Expr{ 1263 Typ: typMap[childColumnName], 1264 Expr: &plan.Expr_Col{ 1265 Col: &plan.ColRef{ 1266 RelPos: baseNodeTag, 1267 ColPos: int32(newColPosMap[childColumnName]), 1268 }, 1269 }, 1270 } 1271 rightExpr := &plan.Expr{ 1272 Typ: parentTypMap[parentColumnName], 1273 Expr: &plan.Expr_Col{ 1274 Col: &plan.ColRef{ 1275 RelPos: rightTag, 1276 ColPos: parentPosMap[parentColumnName], 1277 }, 1278 }, 1279 } 1280 condExpr, err := bindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{leftExpr, rightExpr}) 1281 if err != nil { 1282 return err 1283 } 1284 joinConds[i] = condExpr 1285 break 1286 } 1287 } 1288 } 1289 1290 // append project 1291 info.projectList = append(info.projectList, &plan.Expr{ 1292 Typ: parentTypMap[catalog.Row_ID], 1293 Expr: &plan.Expr_Col{ 1294 Col: &plan.ColRef{ 1295 RelPos: rightTag, 1296 ColPos: parentPosMap[catalog.Row_ID], 1297 }, 1298 }, 1299 }) 1300 info.idx = info.idx + 1 1301 1302 // append join node 1303 leftCtx := builder.ctxByNode[info.rootId] 1304 err = joinCtx.mergeContexts(builder.GetContext(), leftCtx, rightCtx) 1305 if err != nil { 1306 return err 1307 } 1308 newRootId := builder.appendNode(&plan.Node{ 1309 NodeType: plan.Node_JOIN, 1310 Children: []int32{info.rootId, rightId}, 1311 JoinType: plan.Node_LEFT, 1312 OnList: joinConds, 1313 }, joinCtx) 1314 bindCtx.binder = NewTableBinder(builder, bindCtx) 1315 info.rootId = newRootId 1316 } 1317 1318 info.parentIdx = append(info.parentIdx, parentIdx) 1319 } 1320 1321 // check for OnDuplicateUpdate 1322 1323 // todo check for replace 1324 1325 return nil 1326 } 1327 1328 func buildValueScan( 1329 isAllDefault bool, 1330 info *dmlSelectInfo, 1331 builder *QueryBuilder, 1332 bindCtx *BindContext, 1333 tableDef *TableDef, 1334 slt *tree.ValuesClause, 1335 updateColumns []string, 1336 colToIdx map[string]int, 1337 ) error { 1338 var err error 1339 lastTag := builder.genNewTag() 1340 colCount := len(updateColumns) 1341 rowsetData := &plan.RowsetData{ 1342 Cols: make([]*plan.ColData, colCount), 1343 } 1344 valueScanTableDef := &plan.TableDef{ 1345 TblId: 0, 1346 Name: "", 1347 Cols: make([]*plan.ColDef, colCount), 1348 } 1349 projectList := make([]*Expr, colCount) 1350 1351 for i, colName := range updateColumns { 1352 col := tableDef.Cols[colToIdx[colName]] 1353 var defExpr *Expr 1354 rows := make([]*Expr, len(slt.Rows)) 1355 if isAllDefault { 1356 defExpr, err := getDefaultExpr(builder.GetContext(), col) 1357 if err != nil { 1358 return err 1359 } 1360 defExpr, err = forceCastExpr(builder.GetContext(), defExpr, col.Typ) 1361 if err != nil { 1362 return err 1363 } 1364 for j := range slt.Rows { 1365 rows[j] = defExpr 1366 } 1367 } else { 1368 binder := NewDefaultBinder(builder.GetContext(), nil, nil, col.Typ, nil) 1369 for j, r := range slt.Rows { 1370 if _, ok := r[i].(*tree.DefaultVal); ok { 1371 defExpr, err = getDefaultExpr(builder.GetContext(), col) 1372 if err != nil { 1373 return err 1374 } 1375 } else { 1376 defExpr, err = binder.BindExpr(r[i], 0, true) 1377 if err != nil { 1378 return err 1379 } 1380 } 1381 defExpr, err = forceCastExpr(builder.GetContext(), defExpr, col.Typ) 1382 if err != nil { 1383 return err 1384 } 1385 rows[j] = defExpr 1386 } 1387 } 1388 rowsetData.Cols[i] = &plan.ColData{ 1389 Data: rows, 1390 } 1391 colName := fmt.Sprintf("column_%d", i) // like MySQL 1392 valueScanTableDef.Cols[i] = &plan.ColDef{ 1393 ColId: 0, 1394 Name: colName, 1395 Typ: col.Typ, 1396 } 1397 projectList[i] = &plan.Expr{ 1398 Typ: col.Typ, 1399 Expr: &plan.Expr_Col{ 1400 Col: &plan.ColRef{ 1401 RelPos: lastTag, 1402 ColPos: int32(i), 1403 }, 1404 }, 1405 } 1406 } 1407 info.rootId = builder.appendNode(&plan.Node{ 1408 NodeType: plan.Node_VALUE_SCAN, 1409 RowsetData: rowsetData, 1410 TableDef: valueScanTableDef, 1411 BindingTags: []int32{lastTag}, 1412 }, bindCtx) 1413 err = builder.addBinding(info.rootId, tree.AliasClause{ 1414 Alias: "_ValueScan", 1415 }, bindCtx) 1416 if err != nil { 1417 return err 1418 } 1419 1420 lastTag = builder.genNewTag() 1421 info.rootId = builder.appendNode(&plan.Node{ 1422 NodeType: plan.Node_PROJECT, 1423 ProjectList: projectList, 1424 Children: []int32{info.rootId}, 1425 BindingTags: []int32{lastTag}, 1426 }, bindCtx) 1427 1428 return nil 1429 }