Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: support add column with default value expression (#52677) #52829

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion pkg/ddl/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -1948,7 +1948,9 @@ func modifyColsFromNull2NotNull(w *worker, dbInfo *model.DBInfo, tblInfo *model.
func generateOriginDefaultValue(col *model.ColumnInfo, ctx sessionctx.Context) (any, error) {
var err error
odValue := col.GetDefaultValue()
if odValue == nil && mysql.HasNotNullFlag(col.GetFlag()) {
if odValue == nil && mysql.HasNotNullFlag(col.GetFlag()) ||
// It's for drop column and modify column.
(col.DefaultIsExpr && odValue != strings.ToUpper(ast.CurrentTimestamp) && ctx == nil) {
switch col.GetType() {
// Just use enum field's first element for OriginDefaultValue.
case mysql.TypeEnum:
Expand Down Expand Up @@ -1979,6 +1981,29 @@ func generateOriginDefaultValue(col *model.ColumnInfo, ctx sessionctx.Context) (
} else if col.GetType() == mysql.TypeDatetime {
odValue = types.NewTime(types.FromGoTime(t), col.GetType(), col.GetDecimal()).String()
}
return odValue, nil
}

if col.DefaultIsExpr && ctx != nil {
valStr, ok := odValue.(string)
if !ok {
return nil, dbterror.ErrDefValGeneratedNamedFunctionIsNotAllowed.GenWithStackByArgs(col.Name.String())
}
oldValue := strings.ToLower(valStr)
// It's checked in getFuncCallDefaultValue.
if !strings.Contains(oldValue, fmt.Sprintf("%s(%s(),", ast.DateFormat, ast.Now)) &&
!strings.Contains(oldValue, ast.StrToDate) {
return nil, errors.Trace(dbterror.ErrBinlogUnsafeSystemFunction)
}

defVal, err := table.GetColDefaultValue(ctx.GetExprCtx(), col)
if err != nil {
return nil, errors.Trace(err)
}
odValue, err = defVal.ToString()
if err != nil {
return nil, errors.Trace(err)
}
}
return odValue, nil
}
Expand Down
51 changes: 38 additions & 13 deletions pkg/ddl/column_type_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ func TestChangingColOriginDefaultValueAfterAddColAndCastFail(t *testing.T) {
originalHook := dom.DDL().GetHook()
hook := &callback.TestDDLCallback{Do: dom}
var checkErr error
var firstJobID int64
hook.OnJobRunBeforeExported = func(job *model.Job) {
if checkErr != nil {
return
Expand All @@ -495,34 +496,58 @@ func TestChangingColOriginDefaultValueAfterAddColAndCastFail(t *testing.T) {
return
}

if firstJobID == 0 {
firstJobID = job.ID
}
if job.SchemaState == model.StateWriteOnly || job.SchemaState == model.StateWriteReorganization {
tbl := external.GetTableByName(t, tk, "test", "t")
if len(tbl.WritableCols()) != 4 {
errMsg := fmt.Sprintf("cols len:%v", len(tbl.WritableCols()))
errMsg := fmt.Sprintf("job ID:%d, cols len:%v", job.ID, len(tbl.WritableCols()))
checkErr = errors.New("assert the writable column number error" + errMsg)
return
}
originalDV := fmt.Sprintf("%v", tbl.WritableCols()[3].OriginDefaultValue)
expectVal := "0000-00-00 00:00:00"
if originalDV != expectVal {
errMsg := fmt.Sprintf("expect: %v, got: %v", expectVal, originalDV)
checkErr = errors.New("assert the write only column origin default value error" + errMsg)
return
// modify column x
if job.ID == firstJobID {
originalDV := fmt.Sprintf("%v", tbl.WritableCols()[3].OriginDefaultValue)
expectVal := "0000-00-00 00:00:00"
if originalDV != expectVal {
errMsg := fmt.Sprintf("job ID:%d, expect: %v, got: %v", job.ID, expectVal, originalDV)
checkErr = errors.New("assert the write only column origin default value error" + errMsg)
return
}
// The cast value will be inserted into changing column too.
_, err := tk1.Exec("UPDATE t SET a = '18apf' WHERE x = '' AND a = 'mul'")
if err != nil {
checkErr = err
return
}
}
// The casted value will be inserted into changing column too.
_, err := tk1.Exec("UPDATE t SET a = '18apf' WHERE x = '' AND a = 'mul'")
if err != nil {
checkErr = err
return
// modify column b
if job.ID == firstJobID+1 {
originalDV := fmt.Sprintf("%v", tbl.WritableCols()[3].OriginDefaultValue)
expectVal := ""
if originalDV != expectVal {
errMsg := fmt.Sprintf("job ID:%d, expect: %v, got: %v", job.ID, expectVal, originalDV)
checkErr = errors.New("assert the write only column origin default value error" + errMsg)
return
}
// The cast value will be inserted into changing column too.
_, err := tk1.Exec("UPDATE t SET a = '18apf' WHERE a = '1'")
if err != nil {
checkErr = err
return
}
}
}
}

dom.DDL().SetHook(hook)
tk.MustExec("alter table t modify column x DATETIME NULL DEFAULT '3771-02-28 13:00:11' AFTER b;")
tk.MustExec("insert into t(a) value('1')")
tk.MustExec("alter table t modify column b varchar(256) default (REPLACE(UPPER(UUID()), '-', ''));")
dom.DDL().SetHook(originalHook)
require.NoError(t, checkErr)
tk.MustQuery("select * from t order by a").Check(testkit.Rows())
tk.MustQuery("select * from t order by a").Check(testkit.Rows("18apf -729850476163 3771-02-28 13:00:11"))
tk.MustExec("drop table if exists t")
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/ddl/db_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ func TestDropNotNullColumn(t *testing.T) {
tk.MustExec("insert into t2 values(3, '11:22:33')")
tk.MustExec("create table t3 (id int, d json not null)")
tk.MustExec("insert into t3 values(4, d)")
tk.MustExec("create table t4 (id int, e varchar(256) default (REPLACE(UPPER(UUID()), '-', '')) not null)")
tk.MustExec("insert into t4 values(4, 3)")

tk1 := testkit.NewTestKit(t, store)
tk1.MustExec("use test")
Expand All @@ -161,6 +163,8 @@ func TestDropNotNullColumn(t *testing.T) {
_, checkErr = tk1.Exec("insert into t2 set id = 3")
case 3:
_, checkErr = tk1.Exec("insert into t3 set id = 4")
case 4:
_, checkErr = tk1.Exec("insert into t4 set id = 5")
}
}
}
Expand All @@ -177,6 +181,9 @@ func TestDropNotNullColumn(t *testing.T) {
sqlNum++
tk.MustExec("alter table t3 drop column d")
require.NoError(t, checkErr)
sqlNum++
tk.MustExec("alter table t4 drop column e")
require.NoError(t, checkErr)
d.SetHook(originalCallback)
tk.MustExec("drop table t, t1, t2, t3")
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,12 @@ func TestDefaultValueAsExpressions(t *testing.T) {
tk.MustExec("create table t2(c int(10), c1 varchar(256) default (REPLACE(UPPER(UUID()), '-', '')), index idx(c1));")
tk.MustExec("insert into t2(c) values (1),(2),(3);")
tk.MustGetErrCode("alter table t2 modify column c1 varchar(30) default 'xx';", errno.WarnDataTruncated)
// test add column for enum
nowStr := time.Now().Format("2006-01")
sql := fmt.Sprintf("alter table t2 add column c3 enum('%v','n')", nowStr) + " default (date_format(now(),'%Y-%m'))"
tk.MustExec(sql)
tk.MustExec("insert into t2(c) values (4);")
tk.MustQuery("select c3 from t2").Check(testkit.Rows(nowStr, nowStr, nowStr, nowStr))
}

func TestChangingDBCharset(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4311,7 +4311,7 @@ func CreateNewColumn(ctx sessionctx.Context, schema *model.DBInfo, spec *ast.Alt
return nil, errors.Trace(err)
}
return nil, errors.Trace(dbterror.ErrAddColumnWithSequenceAsDefault.GenWithStackByArgs(specNewColumn.Name.Name.O))
case ast.Rand, ast.UUID, ast.UUIDToBin, ast.Replace, ast.Upper, ast.DateFormat, ast.StrToDate:
case ast.Rand, ast.UUID, ast.UUIDToBin, ast.Replace, ast.Upper:
return nil, errors.Trace(dbterror.ErrBinlogUnsafeSystemFunction.GenWithStackByArgs())
}
}
Expand Down
64 changes: 40 additions & 24 deletions tests/integrationtest/r/ddl/default_as_expression.result
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ create table t6 (c int(10), c1 varchar(256) default (date_format(now(),'%b %d %Y
Error 3770 (HY000): Default value expression of column 'c1' contains a disallowed function: `KindString %b %d %Y %h:%i %p`.
create table t7 (c int(10), c1 varchar(256) default (date_format(now(),'%Y-%m-%d %H:%i:%s %p')));
Error 3770 (HY000): Default value expression of column 'c1' contains a disallowed function: `KindString %Y-%m-%d %H:%i:%s %p`.
alter table t0 add column c2 date default (date_format(now(),'%Y-%m'));
Error 1674 (HY000): Statement is unsafe because it uses a system function that may return a different value on the slave
SET @x := NOW();
insert into t0(c) values (1);
insert into t0 values (2, default);
Expand Down Expand Up @@ -63,15 +61,23 @@ t2 CREATE TABLE `t2` (
`c` int(10) DEFAULT NULL,
`c1` varchar(256) DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d %H.%i.%s')
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
alter table t0 add column c2 date default (date_format(now(),'%Y-%m'));
Error 1292 (22007): Incorrect datetime value: '2024-04'
alter table t0 add index idx(c1);
alter table t1 add index idx(c1);
insert into t0 values (3, default);
alter table t0 add column c2 date default (date_format(now(),'%Y-%m-%d'));
alter table t0 add column c3 enum('y','n') default (date_format(now(),'%Y-%m-%d'));
Error 1265 (01000): Data truncated for column '%s' at row %d
alter table t0 add column c4 blob default (date_format(now(),'%Y-%m-%d'));
insert into t0 values (3, default, default, default);
insert into t1 values (3, default);
show create table t0;
Table Create Table
t0 CREATE TABLE `t0` (
`c` int(10) DEFAULT NULL,
`c1` varchar(256) DEFAULT date_format(now(), _utf8mb4'%Y-%m'),
`c2` date DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d'),
`c4` blob DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d'),
KEY `idx` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
show create table t1;
Expand All @@ -83,13 +89,15 @@ t1 CREATE TABLE `t1` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
alter table t0 modify column c1 varchar(30) default 'xx';
alter table t1 modify column c1 varchar(30) default 'xx';
insert into t0 values (4, default);
insert into t0 values (4, default, default, default);
insert into t1 values (4, default);
show create table t0;
Table Create Table
t0 CREATE TABLE `t0` (
`c` int(10) DEFAULT NULL,
`c1` varchar(30) DEFAULT 'xx',
`c2` date DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d'),
`c4` blob DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d'),
KEY `idx` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
show create table t1;
Expand All @@ -102,7 +110,7 @@ t1 CREATE TABLE `t1` (
alter table t0 modify column c1 datetime DEFAULT (date_format(now(), '%Y-%m-%d'));
Error 1292 (22007): Incorrect datetime value:<time>
alter table t0 alter column c1 SET DEFAULT (date_format(now(), '%Y-%m-%d'));
insert into t0 values (5, default);
insert into t0 values (5, default, default, default);
alter table t1 modify column c1 datetime DEFAULT (date_format(now(), '%Y-%m-%d'));
Error 1292 (22007): Incorrect datetime value: 'xx'
delete from t1 where c = 4;
Expand All @@ -114,7 +122,9 @@ show create table t0;
Table Create Table
t0 CREATE TABLE `t0` (
`c` int(10) DEFAULT NULL,
`c1` varchar(30) DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d')
`c1` varchar(30) DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d'),
`c2` date DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d'),
`c4` blob DEFAULT date_format(now(), _utf8mb4'%Y-%m-%d')
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
show create table t1;
Table Create Table
Expand Down Expand Up @@ -229,13 +239,11 @@ create table t6 (c int(10), c1 varchar(32) default (str_to_date(upper('1980-01-0
Error 3770 (HY000): Default value expression of column 'c1' contains a disallowed function: `str_to_date with disallowed args`.
create table t6 (c int(10), c1 varchar(32) default (str_to_date('1980-01-01',upper('%Y-%m-%d'))));
Error 3770 (HY000): Default value expression of column 'c1' contains a disallowed function: `str_to_date with disallowed args`.
alter table t0 add column c3 varchar(32) default (str_to_date('1980-01-01','%Y-%m-%d'));
Error 1674 (HY000): Statement is unsafe because it uses a system function that may return a different value on the slave
alter table t0 add column c3 datetime default (str_to_date('1980-01-01','%Y-%m-%d'));
alter table t0 add column c4 int default (str_to_date('1980-01-01','%Y-%m-%d'));
Error 1674 (HY000): Statement is unsafe because it uses a system function that may return a different value on the slave
insert into t0(c) values (1),(2),(3);
insert into t1(c) values (1),(2),(3);
insert into t0 values (4, default, default);
insert into t0 values (4, default, default, default, default);
insert into t1 values (4, default, default);
insert into t3(c) values (1);
Error 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00'
Expand All @@ -249,11 +257,11 @@ set session sql_mode=@sqlMode;
insert into t2(c) values (5);
Error 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00'
select * from t0;
c c1 c2
1 1980-01-01 9999-01-01
2 1980-01-01 9999-01-01
3 1980-01-01 9999-01-01
4 1980-01-01 9999-01-01
c c1 c2 c3 c4
1 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
2 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
3 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
4 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
select * from t1;
c c1 c2
1 19800101 99990101
Expand All @@ -272,6 +280,8 @@ t0 CREATE TABLE `t0` (
`c` int(10) DEFAULT NULL,
`c1` varchar(32) DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
`c2` date DEFAULT str_to_date(_utf8mb4'9999-01-01', _utf8mb4'%Y-%m-%d'),
`c3` datetime DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
`c4` int(11) DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
KEY `idx` (`c`,`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
show create table t1;
Expand All @@ -291,14 +301,16 @@ t2 CREATE TABLE `t2` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
alter table t0 add index idx1(c1);
alter table t1 add unique index idx1(c, c1);
insert into t0 values (5, default, default);
insert into t0 values (5, default, default, default, default);
insert into t1 values (5, default, default);
show create table t0;
Table Create Table
t0 CREATE TABLE `t0` (
`c` int(10) DEFAULT NULL,
`c1` varchar(32) DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
`c2` date DEFAULT str_to_date(_utf8mb4'9999-01-01', _utf8mb4'%Y-%m-%d'),
`c3` datetime DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
`c4` int(11) DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
KEY `idx` (`c`,`c1`),
KEY `idx1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
Expand All @@ -313,14 +325,16 @@ t1 CREATE TABLE `t1` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
alter table t0 alter column c2 set default (current_date());
alter table t1 modify column c1 varchar(30) default 'xx';
insert into t0 values (6, default, default);
insert into t0 values (6, default, default, default, default);
insert into t1 values (6, default, default);
show create table t0;
Table Create Table
t0 CREATE TABLE `t0` (
`c` int(10) DEFAULT NULL,
`c1` varchar(32) DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
`c2` date DEFAULT CURRENT_DATE,
`c3` datetime DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
`c4` int(11) DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
KEY `idx` (`c`,`c1`),
KEY `idx1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
Expand All @@ -335,16 +349,16 @@ t1 CREATE TABLE `t1` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
alter table t0 alter column c1 drop default;
alter table t1 modify column c1 varchar(32) default (str_to_date('1980-01-01','%Y-%m-%d'));
insert into t0 values (7, default, default);
insert into t0 values (7, default, default, default, default);
Error 1364 (HY000): Field 'c1' doesn't have a default value
insert into t1 values (7, default, default);
select * from t0 where c < 6;
c c1 c2
1 1980-01-01 9999-01-01
2 1980-01-01 9999-01-01
3 1980-01-01 9999-01-01
4 1980-01-01 9999-01-01
5 1980-01-01 9999-01-01
c c1 c2 c3 c4
1 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
2 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
3 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
4 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
5 1980-01-01 9999-01-01 1980-01-01 00:00:00 19800101
select c, c1 from t0 where c = 6 and c2 = date_format(now(),'%Y-%m-%d');;
c c1
6 1980-01-01
Expand All @@ -371,6 +385,8 @@ Table Create Table
t0 CREATE TABLE `t0` (
`c` int(10) DEFAULT NULL,
`c1` varchar(32),
`c3` datetime DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
`c4` int(11) DEFAULT str_to_date(_utf8mb4'1980-01-01', _utf8mb4'%Y-%m-%d'),
KEY `idx` (`c`,`c1`),
KEY `idx1` (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
Expand Down
Loading