Skip to content

Commit

Permalink
table: fix issue of get default value from column when column doesn't…
Browse files Browse the repository at this point in the history
… have default value (#51309) (#51391)

close #50043, close #51324
  • Loading branch information
ti-chi-bot authored Feb 28, 2024
1 parent 61be008 commit 11c815c
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 20 deletions.
5 changes: 4 additions & 1 deletion ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1619,7 +1619,10 @@ func TestAlterColumn(t *testing.T) {
// tk.MustExec( "alter table test_alter_column alter column d set default null")
tk.MustExec("alter table test_alter_column alter column a drop default")
tk.MustGetErrCode("insert into test_alter_column set b = 'd', c = 'dd'", errno.ErrNoDefaultForField)
tk.MustQuery("select a from test_alter_column").Check(testkit.Rows("111", "222", "222", "123"))
tk.MustGetErrCode("insert into test_alter_column set a = DEFAULT, b = 'd', c = 'dd'", errno.ErrNoDefaultForField)
tk.MustGetErrCode("insert into test_alter_column values (DEFAULT, 'd', 'dd', DEFAULT)", errno.ErrNoDefaultForField)
tk.MustExec("insert into test_alter_column set a = NULL, b = 'd', c = 'dd'")
tk.MustQuery("select a from test_alter_column").Check(testkit.Rows("111", "222", "222", "123", "<nil>"))

// for failing tests
sql := "alter table db_not_exist.test_alter_column alter column b set default 'c'"
Expand Down
101 changes: 101 additions & 0 deletions executor/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6340,3 +6340,104 @@ func TestProcessInfoOfSubQuery(t *testing.T) {
tk2.MustQuery("select 1 from information_schema.processlist where TxnStart != '' and info like 'select%sleep% from t%'").Check(testkit.Rows("1"))
wg.Wait()
}

func TestIssue50043(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
// Test simplified case by update.
tk.MustExec("use test")
tk.MustExec("create table t (c1 boolean ,c2 decimal ( 37 , 17 ), unique key idx1 (c1 ,c2),unique key idx2 ( c1 ));")
tk.MustExec("insert into t values (0,NULL);")
tk.MustExec("alter table t alter column c2 drop default;")
tk.MustExec("update t set c2 = 5 where c1 = 0;")
tk.MustQuery("select * from t order by c1,c2").Check(testkit.Rows("0 5.00000000000000000"))

// Test simplified case by insert on duplicate key update.
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (c1 boolean ,c2 decimal ( 37 , 17 ), unique key idx1 (c1 ,c2));")
tk.MustExec("alter table t alter column c2 drop default;")
tk.MustExec("alter table t add unique key idx4 ( c1 );")
tk.MustExec("insert into t values (0, NULL), (1, 1);")
tk.MustExec("insert into t values (0, 2) ,(1, 3) on duplicate key update c2 = 5;")
tk.MustQuery("show warnings").Check(testkit.Rows())
tk.MustQuery("select * from t order by c1,c2").Check(testkit.Rows("0 5.00000000000000000", "1 5.00000000000000000"))

// Test Issue 50043.
tk.MustExec("drop table if exists t")
tk.MustExec("create table t (c1 boolean ,c2 decimal ( 37 , 17 ), unique key idx1 (c1 ,c2));")
tk.MustExec("alter table t alter column c2 drop default;")
tk.MustExec("alter table t add unique key idx4 ( c1 );")
tk.MustExec("insert into t values (0, NULL), (1, 1);")
tk.MustExec("insert ignore into t values (0, 2) ,(1, 3) on duplicate key update c2 = 5, c1 = 0")
tk.MustQuery("select * from t order by c1,c2").Check(testkit.Rows("0 5.00000000000000000", "1 1.00000000000000000"))
}

func TestIssue51324(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("create table t (id int key, a int, b enum('a', 'b'))")
tk.MustGetErrMsg("insert into t values ()", "[table:1364]Field 'id' doesn't have a default value")
tk.MustExec("insert into t set id = 1")
tk.MustExec("insert into t set id = 2, a = NULL, b = NULL")
tk.MustExec("insert into t set id = 3, a = DEFAULT, b = DEFAULT")
tk.MustQuery("select * from t order by id").Check(testkit.Rows("1 <nil> <nil>", "2 <nil> <nil>", "3 <nil> <nil>"))

tk.MustExec("alter table t alter column a drop default")
tk.MustExec("alter table t alter column b drop default")
tk.MustGetErrMsg("insert into t set id = 4;", "[table:1364]Field 'a' doesn't have a default value")
tk.MustExec("insert into t set id = 5, a = NULL, b = NULL;")
tk.MustGetErrMsg("insert into t set id = 6, a = DEFAULT, b = DEFAULT;", "[table:1364]Field 'a' doesn't have a default value")
tk.MustQuery("select * from t order by id").Check(testkit.Rows("1 <nil> <nil>", "2 <nil> <nil>", "3 <nil> <nil>", "5 <nil> <nil>"))

tk.MustExec("insert ignore into t set id = 4;")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value"))
tk.MustExec("insert ignore into t set id = 6, a = DEFAULT, b = DEFAULT;")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value"))
tk.MustQuery("select * from t order by id").Check(testkit.Rows("1 <nil> <nil>", "2 <nil> <nil>", "3 <nil> <nil>", "4 <nil> <nil>", "5 <nil> <nil>", "6 <nil> <nil>"))
tk.MustExec("update t set id = id + 10")
tk.MustQuery("show warnings").Check(testkit.Rows())
tk.MustQuery("select * from t order by id").Check(testkit.Rows("11 <nil> <nil>", "12 <nil> <nil>", "13 <nil> <nil>", "14 <nil> <nil>", "15 <nil> <nil>", "16 <nil> <nil>"))

// Test not null case.
tk.MustExec("drop table t")
tk.MustExec("create table t (id int key, a int not null, b enum('a', 'b') not null)")
tk.MustGetErrMsg("insert into t values ()", "[table:1364]Field 'id' doesn't have a default value")
tk.MustGetErrMsg("insert into t set id = 1", "[table:1364]Field 'a' doesn't have a default value")
tk.MustGetErrMsg("insert into t set id = 2, a = NULL, b = NULL", "[table:1048]Column 'a' cannot be null")
tk.MustGetErrMsg("insert into t set id = 2, a = 2, b = NULL", "[table:1048]Column 'b' cannot be null")
tk.MustGetErrMsg("insert into t set id = 3, a = DEFAULT, b = DEFAULT", "[table:1364]Field 'a' doesn't have a default value")
tk.MustExec("alter table t alter column a drop default")
tk.MustExec("alter table t alter column b drop default")
tk.MustExec("insert ignore into t set id = 4;")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value"))
tk.MustExec("insert ignore into t set id = 5, a = NULL, b = NULL;")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'a' cannot be null", "Warning 1048 Column 'b' cannot be null"))
tk.MustExec("insert ignore into t set id = 6, a = 6, b = NULL;")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'b' cannot be null"))
tk.MustExec("insert ignore into t set id = 7, a = DEFAULT, b = DEFAULT;")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value"))
tk.MustQuery("select * from t order by id").Check(testkit.Rows("4 0 a", "5 0 ", "6 6 ", "7 0 a"))

// Test add column with OriginDefaultValue case.
tk.MustExec("drop table t")
tk.MustExec("create table t (id int, unique key idx (id))")
tk.MustExec("insert into t values (1)")
tk.MustExec("alter table t add column a int default 1")
tk.MustExec("alter table t add column b int default null")
tk.MustExec("alter table t add column c int not null")
tk.MustExec("alter table t add column d int not null default 1")
tk.MustExec("insert ignore into t (id) values (2)")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'c' doesn't have a default value"))
tk.MustExec("insert ignore into t (id) values (1),(2) on duplicate key update id = id+10")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'c' doesn't have a default value"))
tk.MustExec("alter table t alter column a drop default")
tk.MustExec("alter table t alter column b drop default")
tk.MustExec("alter table t alter column c drop default")
tk.MustExec("alter table t alter column d drop default")
tk.MustExec("insert ignore into t (id) values (3)")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value", "Warning 1364 Field 'b' doesn't have a default value", "Warning 1364 Field 'c' doesn't have a default value", "Warning 1364 Field 'd' doesn't have a default value"))
tk.MustExec("insert ignore into t (id) values (11),(12),(3) on duplicate key update id = id+10")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value", "Warning 1364 Field 'b' doesn't have a default value", "Warning 1364 Field 'c' doesn't have a default value", "Warning 1364 Field 'd' doesn't have a default value"))
tk.MustQuery("select * from t order by id").Check(testkit.Rows("13 <nil> <nil> 0 0", "21 1 <nil> 0 1", "22 1 <nil> 0 1"))
}
3 changes: 3 additions & 0 deletions executor/insert_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,9 @@ func (e *InsertValues) getColDefaultValue(idx int, col *table.Column) (d types.D
if col.DefaultIsExpr && col.DefaultExpr != nil {
defaultVal, err = table.EvalColDefaultExpr(e.ctx, col.ToInfo(), col.DefaultExpr)
} else {
if err := table.CheckNoDefaultValueForInsert(e.ctx.GetSessionVars().StmtCtx, col.ToInfo()); err != nil {
return types.Datum{}, err
}
defaultVal, err = table.GetColDefaultValue(e.ctx, col.ToInfo())
}
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion planner/core/expression_rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2130,7 +2130,7 @@ func (er *expressionRewriter) evalDefaultExpr(v *ast.DefaultExpr) {
}
default:
// for other columns, just use what it is
val, er.err = er.b.getDefaultValue(col)
val, er.err = er.b.getDefaultValue(col, false)
}
if er.err != nil {
return
Expand Down
9 changes: 7 additions & 2 deletions planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3561,14 +3561,19 @@ func genAuthErrForGrantStmt(sctx sessionctx.Context, dbName string) error {
return ErrDBaccessDenied.FastGenByArgs(u, h, dbName)
}

func (b *PlanBuilder) getDefaultValue(col *table.Column) (*expression.Constant, error) {
func (b *PlanBuilder) getDefaultValue(col *table.Column, isInsert bool) (*expression.Constant, error) {
var (
value types.Datum
err error
)
if col.DefaultIsExpr && col.DefaultExpr != nil {
value, err = table.EvalColDefaultExpr(b.ctx, col.ToInfo(), col.DefaultExpr)
} else {
if isInsert {
if err := table.CheckNoDefaultValueForInsert(b.ctx.GetSessionVars().StmtCtx, col.ToInfo()); err != nil {
return nil, err
}
}
value, err = table.GetColDefaultValue(b.ctx, col.ToInfo())
}
if err != nil {
Expand Down Expand Up @@ -3858,7 +3863,7 @@ func (b PlanBuilder) getInsertColExpr(ctx context.Context, insertPlan *Insert, m
// See note in the end of the function. Only default for generated columns are OK.
return nil, nil
}
outExpr, err = b.getDefaultValue(refCol)
outExpr, err = b.getDefaultValue(refCol, true)
case *driver.ValueExpr:
outExpr = &expression.Constant{
Value: x.Datum,
Expand Down
46 changes: 30 additions & 16 deletions table/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,22 @@ func GetColOriginDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo) (ty
return getColDefaultValue(ctx, col, col.GetOriginDefaultValue())
}

// CheckNoDefaultValueForInsert checks if the column has no default value before insert data.
// CheckNoDefaultValueForInsert extracts the check logic from getColDefaultValueFromNil,
// since getColDefaultValueFromNil function is public path and both read/write and other places use it.
// But CheckNoDefaultValueForInsert logic should only check before insert.
func CheckNoDefaultValueForInsert(sc *stmtctx.StatementContext, col *model.ColumnInfo) error {
if mysql.HasNoDefaultValueFlag(col.GetFlag()) && !col.DefaultIsExpr && col.GetDefaultValue() == nil && col.GetType() != mysql.TypeEnum {
if !sc.BadNullAsWarning {
return ErrNoDefaultValue.GenWithStackByArgs(col.Name)
}
if !mysql.HasNotNullFlag(col.GetFlag()) {
sc.AppendWarning(ErrNoDefaultValue.FastGenByArgs(col.Name))
}
}
return nil
}

// GetColDefaultValue gets default value of the column.
func GetColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) {
defaultValue := col.GetDefaultValue()
Expand Down Expand Up @@ -580,38 +596,36 @@ func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVa
}

func getColDefaultValueFromNil(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) {
if !mysql.HasNotNullFlag(col.GetFlag()) && !mysql.HasNoDefaultValueFlag(col.GetFlag()) {
if !mysql.HasNotNullFlag(col.GetFlag()) {
return types.Datum{}, nil
}
if col.GetType() == mysql.TypeEnum {
// For enum type, if no default value and not null is set,
// the default value is the first element of the enum list
if mysql.HasNotNullFlag(col.GetFlag()) {
defEnum, err := types.ParseEnumValue(col.FieldType.GetElems(), 1)
if err != nil {
return types.Datum{}, err
}
return types.NewCollateMysqlEnumDatum(defEnum, col.GetCollate()), nil
defEnum, err := types.ParseEnumValue(col.FieldType.GetElems(), 1)
if err != nil {
return types.Datum{}, err
}
return types.Datum{}, nil
return types.NewCollateMysqlEnumDatum(defEnum, col.GetCollate()), nil
}
if mysql.HasAutoIncrementFlag(col.GetFlag()) && !mysql.HasNoDefaultValueFlag(col.GetFlag()) {
if mysql.HasAutoIncrementFlag(col.GetFlag()) {
// Auto increment column doesn't have default value and we should not return error.
return GetZeroValue(col), nil
}
vars := ctx.GetSessionVars()
sc := vars.StmtCtx
if !vars.StrictSQLMode {
sc.AppendWarning(ErrNoDefaultValue.FastGenByArgs(col.Name))
if mysql.HasNotNullFlag(col.GetFlag()) {
return GetZeroValue(col), nil
}
if mysql.HasNoDefaultValueFlag(col.GetFlag()) {
return types.Datum{}, nil
}
return GetZeroValue(col), nil
}
if sc.BadNullAsWarning {
sc.AppendWarning(ErrColumnCantNull.FastGenByArgs(col.Name))
var err error
if mysql.HasNoDefaultValueFlag(col.GetFlag()) {
err = ErrNoDefaultValue.FastGenByArgs(col.Name)
} else {
err = ErrColumnCantNull.FastGenByArgs(col.Name)
}
sc.AppendWarning(err)
return GetZeroValue(col), nil
}
return types.Datum{}, ErrNoDefaultValue.FastGenByArgs(col.Name)
Expand Down

0 comments on commit 11c815c

Please sign in to comment.