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

executor, infoschema: fix display of on update CURRENT_TIMESTAMP with decimal #11480

Merged
merged 11 commits into from
Aug 2, 2019
8 changes: 8 additions & 0 deletions ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,14 @@ func (s *testIntegrationSuite5) TestMySQLErrorCode(c *C) {
assertErrorCode(c, tk, sql, tmysql.ErrPrimaryCantHaveNull)
sql = "create table t2 (id int auto_increment);"
assertErrorCode(c, tk, sql, tmysql.ErrWrongAutoKey)
sql = "create table t2 (a datetime(2) default current_timestamp(3))"
tangenta marked this conversation as resolved.
Show resolved Hide resolved
assertErrorCode(c, tk, sql, tmysql.ErrInvalidDefault)
sql = "create table t2 (a datetime(2) default current_timestamp(2) on update current_timestamp)"
assertErrorCode(c, tk, sql, tmysql.ErrInvalidOnUpdate)
sql = "create table t2 (a datetime default current_timestamp on update current_timestamp(2))"
assertErrorCode(c, tk, sql, tmysql.ErrInvalidOnUpdate)
sql = "create table t2 (a datetime(2) default current_timestamp(2) on update current_timestamp(3))"
assertErrorCode(c, tk, sql, tmysql.ErrInvalidOnUpdate)

sql = "create table t2 (id int primary key , age int);"
tk.MustExec(sql)
Expand Down
4 changes: 2 additions & 2 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ func columnDefToCol(ctx sessionctx.Context, offset int, colDef *ast.ColumnDef, o
case ast.ColumnOptionOnUpdate:
// TODO: Support other time functions.
if col.Tp == mysql.TypeTimestamp || col.Tp == mysql.TypeDatetime {
if !expression.IsCurrentTimestampExpr(v.Expr) {
if !expression.IsValidCurrentTimestampExpr(v.Expr, colDef.Tp) {
return nil, nil, ErrInvalidOnUpdate.GenWithStackByArgs(col.Name)
}
} else {
Expand Down Expand Up @@ -2527,7 +2527,7 @@ func processColumnOptions(ctx sessionctx.Context, col *table.Column, options []*
case ast.ColumnOptionOnUpdate:
// TODO: Support other time functions.
if col.Tp == mysql.TypeTimestamp || col.Tp == mysql.TypeDatetime {
if !expression.IsCurrentTimestampExpr(opt.Expr) {
if !expression.IsValidCurrentTimestampExpr(opt.Expr, &col.FieldType) {
return ErrInvalidOnUpdate.GenWithStackByArgs(col.Name)
}
} else {
Expand Down
1 change: 1 addition & 0 deletions executor/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ func (e *ShowExec) fetchShowCreateTable() error {
}
if mysql.HasOnUpdateNowFlag(col.Flag) {
buf.WriteString(" ON UPDATE CURRENT_TIMESTAMP")
buf.WriteString(table.OptionalFsp(&col.FieldType))
}
}
if len(col.Comment) > 0 {
Expand Down
10 changes: 8 additions & 2 deletions executor/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func (s *testSuite2) TestShow2(c *C) {
c_timestamp timestamp,
c_timestamp_default timestamp default current_timestamp,
c_timestamp_default_3 timestamp(3) default current_timestamp(3),
c_timestamp_default_4 timestamp(3) default current_timestamp(3) on update current_timestamp(3),
c_blob blob,
c_tinyblob tinyblob,
c_mediumblob mediumblob,
Expand Down Expand Up @@ -233,6 +234,7 @@ func (s *testSuite2) TestShow2(c *C) {
"[c_timestamp timestamp <nil> YES <nil> select,insert,update,references ]\n" +
"[c_timestamp_default timestamp <nil> YES CURRENT_TIMESTAMP select,insert,update,references ]\n" +
"[c_timestamp_default_3 timestamp(3) <nil> YES CURRENT_TIMESTAMP(3) select,insert,update,references ]\n" +
"[c_timestamp_default_4 timestamp(3) <nil> YES CURRENT_TIMESTAMP(3) DEFAULT_GENERATED on update CURRENT_TIMESTAMP(3) select,insert,update,references ]\n" +
"[c_blob blob <nil> YES <nil> select,insert,update,references ]\n" +
"[c_tinyblob tinyblob <nil> YES <nil> select,insert,update,references ]\n" +
"[c_mediumblob mediumblob <nil> YES <nil> select,insert,update,references ]\n" +
Expand Down Expand Up @@ -488,15 +490,19 @@ func (s *testSuite2) TestShowCreateTable(c *C) {
"`b` timestamp(3) default current_timestamp(3),\n" +
"`c` datetime default current_timestamp,\n" +
"`d` datetime(4) default current_timestamp(4),\n" +
"`e` varchar(20) default 'cUrrent_tImestamp')")
"`e` varchar(20) default 'cUrrent_tImestamp',\n" +
"`f` datetime(2) default current_timestamp(2) on update current_timestamp(2),\n" +
"`g` timestamp(2) default current_timestamp(2) on update current_timestamp(2))")
tk.MustQuery("show create table `t`").Check(testutil.RowsWithSep("|",
""+
"t CREATE TABLE `t` (\n"+
" `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n"+
" `b` timestamp(3) DEFAULT CURRENT_TIMESTAMP(3),\n"+
" `c` datetime DEFAULT CURRENT_TIMESTAMP,\n"+
" `d` datetime(4) DEFAULT CURRENT_TIMESTAMP(4),\n"+
" `e` varchar(20) DEFAULT 'cUrrent_tImestamp'\n"+
" `e` varchar(20) DEFAULT 'cUrrent_tImestamp',\n"+
" `f` datetime(2) DEFAULT CURRENT_TIMESTAMP(2) ON UPDATE CURRENT_TIMESTAMP(2),\n"+
" `g` timestamp(2) DEFAULT CURRENT_TIMESTAMP(2) ON UPDATE CURRENT_TIMESTAMP(2)\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin",
))
tk.MustExec("drop table t")
Expand Down
22 changes: 17 additions & 5 deletions expression/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,24 @@ func boolToInt64(v bool) int64 {
return 0
}

// IsCurrentTimestampExpr returns whether e is CurrentTimestamp expression.
func IsCurrentTimestampExpr(e ast.ExprNode) bool {
if fn, ok := e.(*ast.FuncCallExpr); ok && fn.FnName.L == ast.CurrentTimestamp {
return true
// IsValidCurrentTimestampExpr returns true if exprNode is a valid CurrentTimestamp expression.
// Here `valid` means it is consistent with the given fieldType's Decimal.
func IsValidCurrentTimestampExpr(exprNode ast.ExprNode, fieldType *types.FieldType) bool {
fn, isFuncCall := exprNode.(*ast.FuncCallExpr)
if !isFuncCall || fn.FnName.L != ast.CurrentTimestamp {
return false
}
return false

containsArg := len(fn.Args) > 0
// Fsp represents fractional seconds precision.
containsFsp := fieldType != nil && fieldType.Decimal > 0
var isConsistent bool
if containsArg {
v, ok := fn.Args[0].(*driver.ValueExpr)
isConsistent = ok && fieldType != nil && v.Datum.GetInt64() == int64(fieldType.Decimal)
}

return (containsArg && isConsistent) || (!containsArg && !containsFsp)
tangenta marked this conversation as resolved.
Show resolved Hide resolved
}

// GetTimeValue gets the time value with type tp.
Expand Down
24 changes: 21 additions & 3 deletions expression/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package expression

import (
driver "github.com/pingcap/tidb/types/parser_driver"
"strings"
"time"

Expand Down Expand Up @@ -108,11 +109,28 @@ func (s *testExpressionSuite) TestGetTimeValue(c *C) {

func (s *testExpressionSuite) TestIsCurrentTimestampExpr(c *C) {
defer testleak.AfterTest(c)()
v := IsCurrentTimestampExpr(ast.NewValueExpr("abc"))
c.Assert(v, IsFalse)
buildTimestampFuncCallExpr := func(i int64) *ast.FuncCallExpr {
var args []ast.ExprNode
if i != 0 {
args = []ast.ExprNode{&driver.ValueExpr{Datum: types.NewIntDatum(i)}}
}
return &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP"), Args: args}
}

v = IsCurrentTimestampExpr(&ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")})
v := IsValidCurrentTimestampExpr(ast.NewValueExpr("abc"), nil)
c.Assert(v, IsFalse)
v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(0), nil)
c.Assert(v, IsTrue)
v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(3), &types.FieldType{Decimal: 3})
c.Assert(v, IsTrue)
v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(1), &types.FieldType{Decimal: 3})
c.Assert(v, IsFalse)
v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(0), &types.FieldType{Decimal: 3})
c.Assert(v, IsFalse)
v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(2), &types.FieldType{Decimal: 0})
c.Assert(v, IsFalse)
v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(2), nil)
c.Assert(v, IsFalse)
}

func (s *testExpressionSuite) TestCurrentTimestampTimeZone(c *C) {
Expand Down
12 changes: 11 additions & 1 deletion table/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package table

import (
"fmt"
"strconv"
"strings"
"time"
"unicode/utf8"
Expand Down Expand Up @@ -271,7 +272,7 @@ func NewColDesc(col *Column) *ColDesc {
} else if mysql.HasOnUpdateNowFlag(col.Flag) {
//in order to match the rules of mysql 8.0.16 version
//see https://github.com/pingcap/tidb/issues/10337
extra = "DEFAULT_GENERATED on update CURRENT_TIMESTAMP"
extra = "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + OptionalFsp(&col.FieldType)
} else if col.IsGenerated() {
if col.GeneratedStored {
extra = "STORED GENERATED"
Expand Down Expand Up @@ -490,3 +491,12 @@ func GetZeroValue(col *model.ColumnInfo) types.Datum {
}
return d
}

// OptionalFsp convert a FieldType.Decimal to string.
func OptionalFsp(fieldType *types.FieldType) string {
fsp := fieldType.Decimal
if fsp == 0 {
return ""
}
return "(" + strconv.Itoa(fsp) + ")"
}