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

ddl: support specifying expr rand() as column default value #32608

Merged
merged 32 commits into from
Apr 7, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1720c3e
Support rand() as column default expr.
CbcWestwolf Mar 29, 2022
f86c1b3
Remove ColumnDefaultType
CbcWestwolf Mar 29, 2022
9b2c973
Merge branch 'master' into column_default_expr
CbcWestwolf Mar 30, 2022
0246e99
Merge branch 'master' into column_default_expr
CbcWestwolf Mar 30, 2022
328a043
Fix
CbcWestwolf Mar 31, 2022
e1787f1
Merge branch 'master' into column_default_expr
CbcWestwolf Mar 31, 2022
644bc0e
Fix
CbcWestwolf Mar 31, 2022
d9da08c
Modify grammar.
CbcWestwolf Mar 31, 2022
4940b06
Remove 'ast.Now'.
CbcWestwolf Mar 31, 2022
414a8bc
Merge branch 'master' into column_default_expr
CbcWestwolf Apr 1, 2022
7f1f986
Add more test.
CbcWestwolf Apr 1, 2022
14341dc
Merge branch 'master' into column_default_expr
CbcWestwolf Apr 1, 2022
9b35bf2
Merge branch 'master' into column_default_expr
CbcWestwolf Apr 2, 2022
c4bf8d8
Merge branch 'master' into column_default_expr
CbcWestwolf Apr 2, 2022
aee1f45
Introduce a new errno from MySQL 8.0
CbcWestwolf Apr 2, 2022
efaf2ef
Fix check_dev fail.
CbcWestwolf Apr 2, 2022
99d40fe
Reformat getDefaultValue.
CbcWestwolf Apr 2, 2022
307d733
Fix UT fail.
CbcWestwolf Apr 2, 2022
a53ce57
Merge branch 'master' into column_default_expr
CbcWestwolf Apr 2, 2022
c5b2b87
Update ddl/ddl_api.go
CbcWestwolf Apr 6, 2022
c00e526
Remove the redundant type assertion.
CbcWestwolf Apr 6, 2022
5dcddc9
Update comments.
CbcWestwolf Apr 6, 2022
eec35a3
Introduce NowSymOptionFractionParentheses.
CbcWestwolf Apr 6, 2022
1851df0
Merge branch 'master' into column_default_expr
CbcWestwolf Apr 6, 2022
f9d0aae
Extract getFuncCallDefaultValue from getDefaultValue.
CbcWestwolf Apr 6, 2022
2ccca80
Fix CI fail.
CbcWestwolf Apr 6, 2022
007fce4
getFuncCallDefaultValue.
CbcWestwolf Apr 6, 2022
e098fa4
Introduce ErrBinlogUnsafeSystemFunction.
CbcWestwolf Apr 6, 2022
388d507
Merge branch 'master' into column_default_expr
CbcWestwolf Apr 7, 2022
a289bdb
Update ddl/ddl_api.go
CbcWestwolf Apr 7, 2022
df005bc
Merge branch 'master' into column_default_expr
ti-chi-bot Apr 7, 2022
299bf2e
Merge branch 'master' into column_default_expr
ti-chi-bot Apr 7, 2022
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
35 changes: 35 additions & 0 deletions ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2031,6 +2031,41 @@ func TestDefaultValueIsString(t *testing.T) {
require.Equal(t, "1", tbl.Meta().Columns[0].DefaultValue)
}

func TestDefaultColumnWithRand(t *testing.T) {
// Related issue: https://github.com/pingcap/tidb/issues/10377
store, clean := testkit.CreateMockStoreWithSchemaLease(t, testLease)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t, t1, t2")
tk.MustExec("create table t (c int(10) default (rand()))")
tk.MustExec("create table t1 (c int default (rand()), c1 double default (rand()))")
tk.MustExec("create table t2 (c int default (rand(1)), c1 double default (rand(1)))")
tk.MustExec("alter table t add column c1 double default (rand(2))")
tk.MustExec("insert into t(c) values (1),(2),(3)")
tk.MustExec("insert into t1(c) values (1),(2),(3)")
tk.MustExec("insert into t2(c) values (1),(2),(3)")

queryStmts := []string{
"SELECT c1 from t",
"SELECT c1 from t1",
"SELECT c1 from t2",
}
for _, queryStmt := range queryStmts {
r := tk.MustQuery(queryStmt).Rows()
for _, row := range r {
d, ok := row[0].(float64)
if ok {
require.True(t, 0.0 <= d && d < 1.0, "rand() return a random floating-point value in the range 0 <= v < 1.0.")
}
}
}

// use a non-existent function name
tk.MustGetErrCode("CREATE TABLE t3 (c int, c1 int default a_function_not_supported_yet());", errno.ErrDefValGeneratedNamedFunctionIsNotAllowed)
}

func TestChangingDBCharset(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
Expand Down
85 changes: 53 additions & 32 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,15 +1035,43 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o
return col, constraints, nil
}

// getFuncCallDefaultValue
func getFuncCallDefaultValue(col *table.Column, option *ast.ColumnOption, expr *ast.FuncCallExpr) (interface{}, bool, error) {
switch expr.FnName.L {
case ast.Rand:
if err := expression.VerifyArgsWrapper(ast.Rand, len(expr.Args)); err != nil {
return nil, false, errors.Trace(err)
}
col.DefaultIsExpr = true
var sb strings.Builder
restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes |
format.RestoreSpacesAroundBinaryOperation
restoreCtx := format.NewRestoreCtx(restoreFlags, &sb)
if err := expr.Restore(restoreCtx); err != nil {
return "", false, err
}
return sb.String(), false, nil
case ast.NextVal:
// handle default next value of sequence. (keep the expr string)
str, err := getSequenceDefaultValue(option)
if err != nil {
return nil, false, errors.Trace(err)
}
return str, true, nil
default:
return nil, false, dbterror.ErrDefValGeneratedNamedFunctionIsNotAllowed.GenWithStackByArgs(col.Name.String(), expr.FnName.String())
}
}

// getDefaultValue will get the default value for column.
// 1: get the expr restored string for the column which uses sequence next value as default value.
// 2: get specific default value for the other column.
func getDefaultValue(ctx sessionctx.Context, col *table.Column, c *ast.ColumnOption) (interface{}, bool, error) {
func getDefaultValue(ctx sessionctx.Context, col *table.Column, option *ast.ColumnOption) (interface{}, bool, error) {
// handle default value with function call
tp, fsp := col.FieldType.Tp, col.FieldType.Decimal
if tp == mysql.TypeTimestamp || tp == mysql.TypeDatetime {
switch x := c.Expr.(type) {
case *ast.FuncCallExpr:
if x.FnName.L == ast.CurrentTimestamp {
if x, ok := option.Expr.(*ast.FuncCallExpr); ok {
if x.FnName.L == ast.CurrentTimestamp {
if tp == mysql.TypeTimestamp || tp == mysql.TypeDatetime {
defaultFsp := 0
if len(x.Args) == 1 {
if val := x.Args[0].(*driver.ValueExpr); val != nil {
Expand All @@ -1054,8 +1082,13 @@ func getDefaultValue(ctx sessionctx.Context, col *table.Column, c *ast.ColumnOpt
return nil, false, dbterror.ErrInvalidDefaultValue.GenWithStackByArgs(col.Name.O)
}
}
} else {
return getFuncCallDefaultValue(col, option, x)
}
vd, err := expression.GetTimeValue(ctx, c.Expr, tp, fsp)
}

if tp == mysql.TypeTimestamp || tp == mysql.TypeDatetime {
vd, err := expression.GetTimeValue(ctx, option.Expr, tp, fsp)
value := vd.GetValue()
if err != nil {
return nil, false, dbterror.ErrInvalidDefaultValue.GenWithStackByArgs(col.Name.O)
Expand All @@ -1073,17 +1106,9 @@ func getDefaultValue(ctx sessionctx.Context, col *table.Column, c *ast.ColumnOpt

return value, false, nil
}
// handle default next value of sequence. (keep the expr string)
str, isSeqExpr, err := tryToGetSequenceDefaultValue(c)
if err != nil {
return nil, false, errors.Trace(err)
}
if isSeqExpr {
return str, true, nil
}

// evaluate the non-sequence expr to a certain value.
v, err := expression.EvalAstExpr(ctx, c.Expr)
// evaluate the non-function-call expr to a certain value.
v, err := expression.EvalAstExpr(ctx, option.Expr)
if err != nil {
return nil, false, errors.Trace(err)
}
Expand Down Expand Up @@ -1138,18 +1163,15 @@ func getDefaultValue(ctx sessionctx.Context, col *table.Column, c *ast.ColumnOpt
return val, false, err
}

func tryToGetSequenceDefaultValue(c *ast.ColumnOption) (expr string, isExpr bool, err error) {
if f, ok := c.Expr.(*ast.FuncCallExpr); ok && f.FnName.L == ast.NextVal {
var sb strings.Builder
restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes |
format.RestoreSpacesAroundBinaryOperation
restoreCtx := format.NewRestoreCtx(restoreFlags, &sb)
if err := c.Expr.Restore(restoreCtx); err != nil {
return "", true, err
}
return sb.String(), true, nil
func getSequenceDefaultValue(c *ast.ColumnOption) (expr string, err error) {
var sb strings.Builder
restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes |
format.RestoreSpacesAroundBinaryOperation
restoreCtx := format.NewRestoreCtx(restoreFlags, &sb)
if err := c.Expr.Restore(restoreCtx); err != nil {
return "", err
}
return "", false, nil
return sb.String(), nil
}

// getSetDefaultValue gets the default value for the set type. See https://dev.mysql.com/doc/refman/5.7/en/set.html.
Expand Down Expand Up @@ -3410,11 +3432,10 @@ func checkAndCreateNewColumn(ctx sessionctx.Context, ti ast.Ident, schema *model
// known rows with specific sequence next value under current add column logic.
// More explanation can refer: TestSequenceDefaultLogic's comment in sequence_test.go
if option.Tp == ast.ColumnOptionDefaultValue {
_, isSeqExpr, err := tryToGetSequenceDefaultValue(option)
if err != nil {
return nil, errors.Trace(err)
}
if isSeqExpr {
if f, ok := option.Expr.(*ast.FuncCallExpr); ok && f.FnName.L == ast.NextVal {
if _, err := getSequenceDefaultValue(option); err != nil {
return nil, errors.Trace(err)
}
return nil, errors.Trace(dbterror.ErrAddColumnWithSequenceAsDefault.GenWithStackByArgs(specNewColumn.Name.Name.O))
}
}
Expand Down
1 change: 1 addition & 0 deletions errno/errcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,7 @@ const (
ErrWrongKeyColumnFunctionalIndex = 3761
ErrFunctionalIndexOnField = 3762
ErrGeneratedColumnRowValueIsNotAllowed = 3764
ErrDefValGeneratedNamedFunctionIsNotAllowed = 3770
ErrFKIncompatibleColumns = 3780
ErrFunctionalIndexRowValueIsNotAllowed = 3800
ErrDependentByFunctionalIndex = 3837
Expand Down
1 change: 1 addition & 0 deletions errno/errname.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{
ErrRowInWrongPartition: mysql.Message("Found a row in wrong partition %s", []int{0}),
ErrGeneratedColumnFunctionIsNotAllowed: mysql.Message("Expression of generated column '%s' contains a disallowed function.", nil),
ErrGeneratedColumnRowValueIsNotAllowed: mysql.Message("Expression of generated column '%s' cannot refer to a row value", nil),
ErrDefValGeneratedNamedFunctionIsNotAllowed: mysql.Message("Default value expression of column '%s' contains a disallowed function: `%s`.", nil),
ErrUnsupportedAlterInplaceOnVirtualColumn: mysql.Message("INPLACE ADD or DROP of virtual columns cannot be combined with other ALTER TABLE actions.", nil),
ErrWrongFKOptionForGeneratedColumn: mysql.Message("Cannot define foreign key with %s clause on a generated column.", nil),
ErrBadGeneratedColumn: mysql.Message("The value specified for generated column '%s' in table '%s' is not allowed.", nil),
Expand Down
5 changes: 5 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,11 @@ error = '''
Expression of generated column '%s' cannot refer to a row value
'''

["ddl:3770"]
error = '''
Default value expression of column '%s' contains a disallowed function: `%s`.
'''

["ddl:3780"]
error = '''
Referencing column '%s' in foreign key constraint '%s' are incompatible
Expand Down
Loading