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) #11591

Merged
merged 8 commits into from
Aug 5, 2019
Merged
Show file tree
Hide file tree
Changes from 7 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
8 changes: 8 additions & 0 deletions ddl/db_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,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))"
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 @@ -523,7 +523,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 @@ -2447,7 +2447,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)
}

// 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
24 changes: 14 additions & 10 deletions infoschema/tables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,23 +270,27 @@ func (s *testTableSuite) TestCurrentTimestampAsDefault(c *C) {
c_timestamp_default_3 timestamp(3) default current_timestamp(3),
c_varchar_default varchar(20) default "current_timestamp",
c_varchar_default_3 varchar(20) default "current_timestamp(3)",
c_varchar_default_on_update datetime default current_timestamp on update current_timestamp,
c_varchar_default_on_update_fsp datetime(3) default current_timestamp(3) on update current_timestamp(3),
c_varchar_default_with_case varchar(20) default "cUrrent_tImestamp"
);`)

tk.MustQuery(`SELECT column_name, column_default
tk.MustQuery(`SELECT column_name, column_default, extra
FROM information_schema.COLUMNS
WHERE table_schema = "default_time_test" AND table_name = "default_time_table"
ORDER BY column_name`,
).Check(testkit.Rows(
"c_datetime <nil>",
"c_datetime_default CURRENT_TIMESTAMP",
"c_datetime_default_2 CURRENT_TIMESTAMP(2)",
"c_timestamp <nil>",
"c_timestamp_default CURRENT_TIMESTAMP",
"c_timestamp_default_3 CURRENT_TIMESTAMP(3)",
"c_varchar_default current_timestamp",
"c_varchar_default_3 current_timestamp(3)",
"c_varchar_default_with_case cUrrent_tImestamp",
"c_datetime <nil> ",
"c_datetime_default CURRENT_TIMESTAMP ",
"c_datetime_default_2 CURRENT_TIMESTAMP(2) ",
"c_timestamp <nil> ",
"c_timestamp_default CURRENT_TIMESTAMP ",
"c_timestamp_default_3 CURRENT_TIMESTAMP(3) ",
"c_varchar_default current_timestamp ",
"c_varchar_default_3 current_timestamp(3) ",
"c_varchar_default_on_update CURRENT_TIMESTAMP DEFAULT_GENERATED on update CURRENT_TIMESTAMP",
"c_varchar_default_on_update_fsp CURRENT_TIMESTAMP(3) DEFAULT_GENERATED on update CURRENT_TIMESTAMP(3)",
"c_varchar_default_with_case cUrrent_tImestamp ",
))
tk.MustExec("DROP DATABASE default_time_test")
}
Expand Down
12 changes: 11 additions & 1 deletion table/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package table
import (
"context"
"fmt"
"strconv"
"strings"
"time"
"unicode/utf8"
Expand Down Expand Up @@ -272,7 +273,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 @@ -494,3 +495,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) + ")"
}