diff --git a/pkg/sql/plan/build_constraint_util.go b/pkg/sql/plan/build_constraint_util.go index b1ae065c6719..9e7ef9bb3ddb 100644 --- a/pkg/sql/plan/build_constraint_util.go +++ b/pkg/sql/plan/build_constraint_util.go @@ -351,7 +351,7 @@ func getDmlTableInfo(ctx CompilerContext, tableExprs tree.TableExprs, with *tree return tblInfo, nil } -func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Insert, info *dmlSelectInfo) (bool, map[int]int, bool, map[string]bool, error) { +func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Insert, info *dmlSelectInfo) (bool, map[int]int, bool, map[string]bool, []string, error) { var err error var insertColumns []string tableDef := info.tblInfo.tableDefs[0] @@ -386,7 +386,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse for _, column := range stmt.Columns { colName := string(column) if _, ok := colToIdx[colName]; !ok { - return false, nil, false, nil, moerr.NewBadFieldError(builder.GetContext(), colName, tableDef.Name) + return false, nil, false, nil, nil, moerr.NewBadFieldError(builder.GetContext(), colName, tableDef.Name) } insertColumns = append(insertColumns, colName) } @@ -403,14 +403,14 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse if isAllDefault { for j, row := range slt.Rows { if row != nil { - return false, nil, false, nil, moerr.NewWrongValueCountOnRow(builder.GetContext(), j+1) + return false, nil, false, nil, nil, moerr.NewWrongValueCountOnRow(builder.GetContext(), j+1) } } } else { colCount := len(insertColumns) for j, row := range slt.Rows { if len(row) != colCount { - return false, nil, false, nil, moerr.NewWrongValueCountOnRow(builder.GetContext(), j+1) + return false, nil, false, nil, nil, moerr.NewWrongValueCountOnRow(builder.GetContext(), j+1) } } } @@ -419,7 +419,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse //but it does not work at the case: //insert into a(a) values (); insert into a values (0),(); if isAllDefault && syntaxHasColumnNames { - return false, nil, false, nil, moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns") + return false, nil, false, nil, nil, moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns") } checkInsertPkDup = len(slt.Rows) > 1 if CNPrimaryCheck { @@ -476,7 +476,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse err = buildValueScan(isAllDefault, info, builder, bindCtx, tableDef, slt, insertColumns, colToIdx) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } case *tree.SelectClause: @@ -485,7 +485,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse subCtx := NewBindContext(builder, bindCtx) info.rootId, err = builder.buildSelect(astSlt, subCtx, false) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } case *tree.ParenSelect: @@ -494,23 +494,23 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse subCtx := NewBindContext(builder, bindCtx) info.rootId, err = builder.buildSelect(astSlt, subCtx, false) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } default: - return false, nil, false, nil, moerr.NewInvalidInput(builder.GetContext(), "insert has unknown select statement") + return false, nil, false, nil, nil, moerr.NewInvalidInput(builder.GetContext(), "insert has unknown select statement") } err = builder.addBinding(info.rootId, tree.AliasClause{ Alias: derivedTableName, }, bindCtx) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } lastNode := builder.qry.Nodes[info.rootId] if len(insertColumns) != len(lastNode.ProjectList) { - return false, nil, false, nil, moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns") + return false, nil, false, nil, nil, moerr.NewInvalidInput(builder.GetContext(), "insert values does not match the number of columns") } tag := builder.qry.Nodes[info.rootId].BindingTags[0] @@ -532,12 +532,12 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse if tableDef.Cols[colIdx].Typ.Id == int32(types.T_enum) { projExpr, err = funcCastForEnumType(builder.GetContext(), projExpr, tableDef.Cols[colIdx].Typ) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } } else { projExpr, err = forceCastExpr(builder.GetContext(), projExpr, tableDef.Cols[colIdx].Typ) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } } insertColToExpr[column] = projExpr @@ -582,7 +582,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse } else { defExpr, err := getDefaultExpr(builder.GetContext(), col) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } if col.Typ.AutoIncr && col.Name == tableDef.Pkey.PkeyColName { @@ -677,17 +677,17 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse if _, ok := updateExpr.(*tree.DefaultVal); ok { defExpr, err = getDefaultExpr(builder.GetContext(), col) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } } else { defExpr, err = binder.BindExpr(updateExpr, 0, true) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } } defExpr, err = forceCastExpr(builder.GetContext(), defExpr, col.Typ) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } updateExprs[col.Name] = defExpr } @@ -730,14 +730,14 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse } eqExpr, err := BindFuncExprImplByPlanExpr(builder.GetContext(), "=", []*Expr{leftExpr, rightExpr}) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } if condIdx == 0 { condExpr = eqExpr } else { condExpr, err = BindFuncExprImplByPlanExpr(builder.GetContext(), "and", []*Expr{condExpr, eqExpr}) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } } condIdx++ @@ -748,7 +748,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse } else { joinConds, err = BindFuncExprImplByPlanExpr(builder.GetContext(), "or", []*Expr{joinConds, condExpr}) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } } joinIdx++ @@ -758,7 +758,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse leftCtx := builder.ctxByNode[info.rootId] err = joinCtx.mergeContexts(builder.GetContext(), leftCtx, rightCtx) if err != nil { - return false, nil, false, nil, err + return false, nil, false, nil, nil, err } newRootId := builder.appendNode(&plan.Node{ NodeType: plan.Node_JOIN, @@ -784,7 +784,7 @@ func initInsertStmt(builder *QueryBuilder, bindCtx *BindContext, stmt *tree.Inse } } - return checkInsertPkDup, pkPosInValues, isInsertWithoutAutoPkCol, insertWithoutUniqueKeyMap, nil + return checkInsertPkDup, pkPosInValues, isInsertWithoutAutoPkCol, insertWithoutUniqueKeyMap, insertColumns, nil } func deleteToSelect(builder *QueryBuilder, bindCtx *BindContext, node *tree.Delete, haveConstraint bool, tblInfo *dmlTableInfo) (int32, error) { diff --git a/pkg/sql/plan/build_dml_util.go b/pkg/sql/plan/build_dml_util.go index 5c683900c38d..39a75d3cb2ca 100644 --- a/pkg/sql/plan/build_dml_util.go +++ b/pkg/sql/plan/build_dml_util.go @@ -111,8 +111,8 @@ type deleteNodeInfo struct { func buildInsertPlans( ctx CompilerContext, builder *QueryBuilder, bindCtx *BindContext, objRef *ObjectRef, tableDef *TableDef, lastNodeId int32, - checkInsertPkDup bool, pkFilterExpr []*Expr, newPartitionExpr *Expr, isInsertWithoutAutoPkCol bool, insertWithoutUniqueKeyMap map[string]bool) error { - + checkInsertPkDup bool, pkFilterExpr []*Expr, newPartitionExpr *Expr, isInsertWithoutAutoPkCol bool, insertWithoutUniqueKeyMap map[string]bool, insertColumns []string, +) error { // add plan: -> preinsert -> sink lastNodeId = appendPreInsertNode(builder, bindCtx, objRef, tableDef, lastNodeId, false) @@ -121,7 +121,7 @@ func buildInsertPlans( // make insert plans insertBindCtx := NewBindContext(builder, nil) - err := makeInsertPlan(ctx, builder, insertBindCtx, objRef, tableDef, 0, sourceStep, true, false, checkInsertPkDup, true, pkFilterExpr, newPartitionExpr, isInsertWithoutAutoPkCol, !builder.qry.LoadTag, nil, nil, insertWithoutUniqueKeyMap) + err := makeInsertPlan(ctx, builder, insertBindCtx, objRef, tableDef, 0, sourceStep, true, false, checkInsertPkDup, true, pkFilterExpr, newPartitionExpr, isInsertWithoutAutoPkCol, !builder.qry.LoadTag, nil, nil, insertWithoutUniqueKeyMap, insertColumns) return err } @@ -200,7 +200,7 @@ func buildUpdatePlans(ctx CompilerContext, builder *QueryBuilder, bindCtx *BindC // build insert plan. insertBindCtx := NewBindContext(builder, nil) err = makeInsertPlan(ctx, builder, insertBindCtx, updatePlanCtx.objRef, updatePlanCtx.tableDef, updatePlanCtx.updateColLength, - sourceStep, false, updatePlanCtx.isFkRecursionCall, updatePlanCtx.checkInsertPkDup, updatePlanCtx.updatePkCol, updatePlanCtx.pkFilterExprs, nil, false, true, nil, nil, nil) + sourceStep, false, updatePlanCtx.isFkRecursionCall, updatePlanCtx.checkInsertPkDup, updatePlanCtx.updatePkCol, updatePlanCtx.pkFilterExprs, nil, false, true, nil, nil, nil, nil) return err } @@ -407,7 +407,7 @@ func buildDeletePlans(ctx CompilerContext, builder *QueryBuilder, bindCtx *BindC } } _checkInsertPKDupForHiddenIndexTable := indexdef.Unique // only check PK uniqueness for UK. SK will not check PK uniqueness. - err = makeInsertPlan(ctx, builder, bindCtx, uniqueObjRef, insertUniqueTableDef, 1, preUKStep, false, false, _checkInsertPKDupForHiddenIndexTable, true, nil, nil, false, _checkInsertPKDupForHiddenIndexTable, nil, nil, nil) + err = makeInsertPlan(ctx, builder, bindCtx, uniqueObjRef, insertUniqueTableDef, 1, preUKStep, false, false, _checkInsertPKDupForHiddenIndexTable, true, nil, nil, false, _checkInsertPKDupForHiddenIndexTable, nil, nil, nil, nil) if err != nil { return err } @@ -924,6 +924,7 @@ func makeInsertPlan( indexSourceColTypes []*plan.Type, fuzzymessage *OriginTableMessageForFuzzy, insertWithoutUniqueKeyMap map[string]bool, + insertColumns []string, ) error { var lastNodeId int32 var err error @@ -1005,6 +1006,8 @@ func makeInsertPlan( var originTableMessageForFuzzy *OriginTableMessageForFuzzy + _checkInsertPkDupForHiddenTable := indexdef.Unique // only check PK uniqueness for UK. SK will not check PK uniqueness. + // The way to guarantee the uniqueness of the unique key is to create a hidden table, // with the primary key of the hidden table as the unique key. // package contains some information needed by the fuzzy filter to run background SQL. @@ -1012,25 +1015,55 @@ func makeInsertPlan( originTableMessageForFuzzy = &OriginTableMessageForFuzzy{ ParentTableName: tableDef.Name, } + autoUniqueNameSet := make(map[string]bool) + uniqueNameSet := make(map[string]int) partialUniqueCols := make([]*plan.ColDef, len(indexdef.Parts)) - set := make(map[string]int) + for i, n := range indexdef.Parts { - set[n] = i + uniqueNameSet[n] = i } for _, c := range tableDef.Cols { // sort - if i, ok := set[c.Name]; ok { + if i, ok := uniqueNameSet[c.Name]; ok { partialUniqueCols[i] = c + if c.Typ.AutoIncr { + autoUniqueNameSet[c.Name] = true + } } } originTableMessageForFuzzy.ParentUniqueCols = partialUniqueCols + + if insertColumns != nil && len(autoUniqueNameSet) > 0 { + atLeastOneHasNoValue := false + + // for those unique key + for n := range autoUniqueNameSet { + if atLeastOneHasNoValue { + break + } + + found := false + // check if at least one auto unique has no value + for _, c := range insertColumns { + if c == n { + found = true + } + } + if !found { + atLeastOneHasNoValue = true + } + } + + if atLeastOneHasNoValue { + _checkInsertPkDupForHiddenTable = false // if so, no need to check dup + } + } } - _checkInsertPkDupForHiddenTable := indexdef.Unique // only check PK uniqueness for UK. SK will not check PK uniqueness. colTypes := make([]*plan.Type, len(tableDef.Cols)) for i := range tableDef.Cols { colTypes[i] = tableDef.Cols[i].Typ } - err = makeInsertPlan(ctx, builder, bindCtx, idxRef, idxTableDef, 0, newSourceStep, false, false, checkInsertPkDupForHiddenIndexTable, true, nil, nil, false, _checkInsertPkDupForHiddenTable, colTypes, originTableMessageForFuzzy, nil) + err = makeInsertPlan(ctx, builder, bindCtx, idxRef, idxTableDef, 0, newSourceStep, false, false, checkInsertPkDupForHiddenIndexTable, true, nil, nil, false, _checkInsertPkDupForHiddenTable, colTypes, originTableMessageForFuzzy, nil, insertColumns) if err != nil { return err } @@ -1048,13 +1081,13 @@ func makeInsertPlan( return err } - //if the all fk are fk self refer, the lastNodeId is -1. - //skip fk self refer here + // if the all fk are fk self refer, the lastNodeId is -1. + // skip fk self refer here if lastNodeId >= 0 { lastNode := builder.qry.Nodes[lastNodeId] beginIdx := len(lastNode.ProjectList) - len(tableDef.Fkeys) - //get filter exprs + // get filter exprs rowIdTyp := types.T_Rowid.ToType() filters := make([]*Expr, len(tableDef.Fkeys)) errExpr := makePlan2StringConstExprWithType("Cannot add or update a child row: a foreign key constraint fails") @@ -1064,7 +1097,7 @@ func makeInsertPlan( Expr: &plan.Expr_Col{ Col: &plan.ColRef{ ColPos: int32(beginIdx + i), - //Name: catalog.Row_ID, + // Name: catalog.Row_ID, }, }, } diff --git a/pkg/sql/plan/build_insert.go b/pkg/sql/plan/build_insert.go index c8d0b202c1a5..7211ab9932ac 100644 --- a/pkg/sql/plan/build_insert.go +++ b/pkg/sql/plan/build_insert.go @@ -75,7 +75,7 @@ func buildInsert(stmt *tree.Insert, ctx CompilerContext, isReplace bool, isPrepa builder.haveOnDuplicateKey = len(stmt.OnDuplicateUpdate) > 0 bindCtx := NewBindContext(builder, nil) - checkInsertPkDup, pkPosInValues, isInsertWithoutAutoPkCol, insertWithoutUniqueKeyMap, err := initInsertStmt(builder, bindCtx, stmt, rewriteInfo) + checkInsertPkDup, pkPosInValues, isInsertWithoutAutoPkCol, insertWithoutUniqueKeyMap, insertColumns, err := initInsertStmt(builder, bindCtx, stmt, rewriteInfo) if err != nil { return nil, err } @@ -255,7 +255,7 @@ func buildInsert(stmt *tree.Insert, ctx CompilerContext, isReplace bool, isPrepa query.StmtType = plan.Query_UPDATE } else { - err = buildInsertPlans(ctx, builder, bindCtx, objRef, tableDef, rewriteInfo.rootId, checkInsertPkDup, pkFilterExprs, newPartitionExpr, isInsertWithoutAutoPkCol, insertWithoutUniqueKeyMap) + err = buildInsertPlans(ctx, builder, bindCtx, objRef, tableDef, rewriteInfo.rootId, checkInsertPkDup, pkFilterExprs, newPartitionExpr, isInsertWithoutAutoPkCol, insertWithoutUniqueKeyMap, insertColumns) if err != nil { return nil, err } diff --git a/pkg/sql/plan/build_load.go b/pkg/sql/plan/build_load.go index 8ddde147fead..3290349894b7 100644 --- a/pkg/sql/plan/build_load.go +++ b/pkg/sql/plan/build_load.go @@ -158,7 +158,7 @@ func buildLoad(stmt *tree.Load, ctx CompilerContext, isPrepareStmt bool) (*Plan, // append hidden column to tableDef newTableDef := DeepCopyTableDef(tableDef, true) checkInsertPkDup := false - err = buildInsertPlans(ctx, builder, bindCtx, objRef, newTableDef, lastNodeId, checkInsertPkDup, nil, nil, isInsertWithoutAutoPkCol, nil) + err = buildInsertPlans(ctx, builder, bindCtx, objRef, newTableDef, lastNodeId, checkInsertPkDup, nil, nil, isInsertWithoutAutoPkCol, nil, nil) if err != nil { return nil, err }