[parser] type/compatibility: fix show create table output of generated column (#235)
This commit is contained in:
@ -158,9 +158,15 @@ func (n *BinaryOperationExpr) Restore(ctx *RestoreCtx) error {
|
||||
if err := n.L.Restore(ctx); err != nil {
|
||||
return errors.Annotate(err, "An error occurred when restore BinaryOperationExpr.L")
|
||||
}
|
||||
if ctx.Flags.HasSpacesAroundBinaryOperationFlag() {
|
||||
ctx.WritePlain(" ")
|
||||
}
|
||||
if err := n.Op.Restore(ctx); err != nil {
|
||||
return errors.Annotate(err, "An error occurred when restore BinaryOperationExpr.Op")
|
||||
}
|
||||
if ctx.Flags.HasSpacesAroundBinaryOperationFlag() {
|
||||
ctx.WritePlain(" ")
|
||||
}
|
||||
if err := n.R.Restore(ctx); err != nil {
|
||||
return errors.Annotate(err, "An error occurred when restore BinaryOperationExpr.R")
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ package ast_test
|
||||
import (
|
||||
. "github.com/pingcap/check"
|
||||
. "github.com/pingcap/parser/ast"
|
||||
"github.com/pingcap/parser/format"
|
||||
_ "github.com/pingcap/tidb/types/parser_driver"
|
||||
)
|
||||
|
||||
@ -200,6 +201,24 @@ func (tc *testExpressionsSuite) TestBinaryOperationExpr(c *C) {
|
||||
RunNodeRestoreTest(c, testCases, "select %s", extractNodeFunc)
|
||||
}
|
||||
|
||||
func (tc *testExpressionsSuite) TestBinaryOperationExprWithFlags(c *C) {
|
||||
testCases := []NodeRestoreTestCase{
|
||||
{"'a'!=1", "'a' != 1"},
|
||||
{"a!=1", "`a` != 1"},
|
||||
{"3<5", "3 < 5"},
|
||||
{"10>5", "10 > 5"},
|
||||
{"3+5", "3 + 5"},
|
||||
{"3-5", "3 - 5"},
|
||||
{"a<>5", "`a` != 5"},
|
||||
{"a=1", "`a` = 1"},
|
||||
}
|
||||
extractNodeFunc := func(node Node) Node {
|
||||
return node.(*SelectStmt).Fields.Fields[0].Expr
|
||||
}
|
||||
flags := format.DefaultRestoreFlags | format.RestoreSpacesAroundBinaryOperation
|
||||
RunNodeRestoreTestWithFlags(c, testCases, "select %s", extractNodeFunc, flags)
|
||||
}
|
||||
|
||||
func (tc *testExpressionsSuite) TestParenthesesExpr(c *C) {
|
||||
testCases := []NodeRestoreTestCase{
|
||||
{"(1+2)*3", "(1+2)*3"},
|
||||
|
||||
@ -512,7 +512,7 @@ func (n *FuncCastExpr) Restore(ctx *RestoreCtx) error {
|
||||
return errors.Annotatef(err, "An error occurred while restore FuncCastExpr.Expr")
|
||||
}
|
||||
ctx.WriteKeyWord(" AS ")
|
||||
n.Tp.FormatAsCastType(ctx.In)
|
||||
n.Tp.RestoreAsCastType(ctx)
|
||||
ctx.WritePlain(")")
|
||||
case CastConvertFunction:
|
||||
ctx.WriteKeyWord("CONVERT")
|
||||
@ -521,7 +521,7 @@ func (n *FuncCastExpr) Restore(ctx *RestoreCtx) error {
|
||||
return errors.Annotatef(err, "An error occurred while restore FuncCastExpr.Expr")
|
||||
}
|
||||
ctx.WritePlain(", ")
|
||||
n.Tp.FormatAsCastType(ctx.In)
|
||||
n.Tp.RestoreAsCastType(ctx)
|
||||
ctx.WritePlain(")")
|
||||
case CastBinaryOperator:
|
||||
ctx.WriteKeyWord("BINARY ")
|
||||
|
||||
@ -84,8 +84,8 @@ func (ts *testFunctionsSuite) TestFuncCallExprRestore(c *C) {
|
||||
func (ts *testFunctionsSuite) TestFuncCastExprRestore(c *C) {
|
||||
testCases := []NodeRestoreTestCase{
|
||||
{"CONVERT('Müller' USING UtF8Mb4)", "CONVERT('Müller' USING UTF8MB4)"},
|
||||
{"CONVERT('Müller', CHAR(32) CHARACTER SET UtF8)", "CONVERT('Müller', CHAR(32) CHARACTER SET UtF8)"},
|
||||
{"CAST('test' AS CHAR CHARACTER SET UtF8)", "CAST('test' AS CHAR CHARACTER SET UtF8)"},
|
||||
{"CONVERT('Müller', CHAR(32) CHARACTER SET UtF8)", "CONVERT('Müller', CHAR(32) CHARSET UTF8)"},
|
||||
{"CAST('test' AS CHAR CHARACTER SET UtF8)", "CAST('test' AS CHAR CHARSET UTF8)"},
|
||||
{"BINARY 'New York'", "BINARY 'New York'"},
|
||||
}
|
||||
extractNodeFunc := func(node Node) Node {
|
||||
@ -132,7 +132,7 @@ func (ts *testFunctionsSuite) TestConvert(c *C) {
|
||||
ErrorMessage string
|
||||
}{
|
||||
{`SELECT CONVERT("abc" USING "latin1")`, "latin1", ""},
|
||||
{`SELECT CONVERT("abc" USING laTiN1)`, "laTiN1", ""},
|
||||
{`SELECT CONVERT("abc" USING laTiN1)`, "latin1", ""},
|
||||
{`SELECT CONVERT("abc" USING "binary")`, "binary", ""},
|
||||
{`SELECT CONVERT("abc" USING biNaRy)`, "binary", ""},
|
||||
{`SELECT CONVERT(a USING a)`, "", `[parser:1115]Unknown character set: 'a'`}, // TiDB issue #4436.
|
||||
@ -161,7 +161,7 @@ func (ts *testFunctionsSuite) TestChar(c *C) {
|
||||
ErrorMessage string
|
||||
}{
|
||||
{`SELECT CHAR("abc" USING "latin1")`, "latin1", ""},
|
||||
{`SELECT CHAR("abc" USING laTiN1)`, "laTiN1", ""},
|
||||
{`SELECT CHAR("abc" USING laTiN1)`, "latin1", ""},
|
||||
{`SELECT CHAR("abc" USING "binary")`, "binary", ""},
|
||||
{`SELECT CHAR("abc" USING binary)`, "binary", ""},
|
||||
{`SELECT CHAR(a USING a)`, "", `[parser:1115]Unknown character set: 'a'`},
|
||||
|
||||
@ -103,6 +103,10 @@ type NodeRestoreTestCase struct {
|
||||
}
|
||||
|
||||
func RunNodeRestoreTest(c *C, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node) {
|
||||
RunNodeRestoreTestWithFlags(c, nodeTestCases, template, extractNodeFunc, DefaultRestoreFlags)
|
||||
}
|
||||
|
||||
func RunNodeRestoreTestWithFlags(c *C, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node, flags RestoreFlags) {
|
||||
parser := parser.New()
|
||||
parser.EnableWindowFunc(true)
|
||||
for _, testCase := range nodeTestCases {
|
||||
@ -112,7 +116,7 @@ func RunNodeRestoreTest(c *C, nodeTestCases []NodeRestoreTestCase, template stri
|
||||
comment := Commentf("source %#v", testCase)
|
||||
c.Assert(err, IsNil, comment)
|
||||
var sb strings.Builder
|
||||
err = extractNodeFunc(stmt).Restore(NewRestoreCtx(DefaultRestoreFlags, &sb))
|
||||
err = extractNodeFunc(stmt).Restore(NewRestoreCtx(flags, &sb))
|
||||
c.Assert(err, IsNil, comment)
|
||||
restoreSql := fmt.Sprintf(template, sb.String())
|
||||
comment = Commentf("source %#v; restore %v", testCase, restoreSql)
|
||||
|
||||
@ -216,6 +216,8 @@ const (
|
||||
RestoreNameLowercase
|
||||
RestoreNameDoubleQuotes
|
||||
RestoreNameBackQuotes
|
||||
|
||||
RestoreSpacesAroundBinaryOperation
|
||||
)
|
||||
|
||||
const (
|
||||
@ -271,6 +273,11 @@ func (rf RestoreFlags) HasNameBackQuotesFlag() bool {
|
||||
return rf.has(RestoreNameBackQuotes)
|
||||
}
|
||||
|
||||
// HasSpacesAroundBinaryOperationFlag returns a boolean indicating whether `rf` has `RestoreSpacesAroundBinaryOperation` flag.
|
||||
func (rf RestoreFlags) HasSpacesAroundBinaryOperationFlag() bool {
|
||||
return rf.has(RestoreSpacesAroundBinaryOperation)
|
||||
}
|
||||
|
||||
// RestoreCtx is `Restore` context to hold flags and writer.
|
||||
type RestoreCtx struct {
|
||||
Flags RestoreFlags
|
||||
|
||||
@ -11568,14 +11568,14 @@ yynewstate:
|
||||
case 1088:
|
||||
{
|
||||
// Validate input charset name to keep the same behavior as parser of MySQL.
|
||||
_, _, err := charset.GetCharsetInfo(yyS[yypt-0].item.(string))
|
||||
name, _, err := charset.GetCharsetInfo(yyS[yypt-0].item.(string))
|
||||
if err != nil {
|
||||
yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs(yyS[yypt-0].item))
|
||||
return 1
|
||||
}
|
||||
// Use $1 instead of charset name returned from charset.GetCharsetInfo(),
|
||||
// to keep upper-lower case of input for restore.
|
||||
parser.yyVAL.item = yyS[yypt-0].item
|
||||
// Use charset name returned from charset.GetCharsetInfo(),
|
||||
// to keep lower case of input for generated column restore.
|
||||
parser.yyVAL.item = name
|
||||
}
|
||||
case 1089:
|
||||
{
|
||||
|
||||
@ -5868,14 +5868,14 @@ CharsetName:
|
||||
StringName
|
||||
{
|
||||
// Validate input charset name to keep the same behavior as parser of MySQL.
|
||||
_, _, err := charset.GetCharsetInfo($1.(string))
|
||||
name, _, err := charset.GetCharsetInfo($1.(string))
|
||||
if err != nil {
|
||||
yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs($1))
|
||||
return 1
|
||||
}
|
||||
// Use $1 instead of charset name returned from charset.GetCharsetInfo(),
|
||||
// to keep upper-lower case of input for restore.
|
||||
$$ = $1
|
||||
// Use charset name returned from charset.GetCharsetInfo(),
|
||||
// to keep lower case of input for generated column restore.
|
||||
$$ = name
|
||||
}
|
||||
| binaryType
|
||||
{
|
||||
|
||||
@ -1003,7 +1003,7 @@ func (s *testParserSuite) TestBuiltin(c *C) {
|
||||
{"create table t (row int)", false, ""},
|
||||
|
||||
// for cast with charset
|
||||
{"SELECT *, CAST(data AS CHAR CHARACTER SET utf8) FROM t;", true, "SELECT *,CAST(`data` AS CHAR CHARACTER SET utf8) FROM `t`"},
|
||||
{"SELECT *, CAST(data AS CHAR CHARACTER SET utf8) FROM t;", true, "SELECT *,CAST(`data` AS CHAR CHARSET UTF8) FROM `t`"},
|
||||
|
||||
// for cast as JSON
|
||||
{"SELECT *, CAST(data AS JSON) FROM t;", true, "SELECT *,CAST(`data` AS JSON) FROM `t`"},
|
||||
@ -1198,7 +1198,7 @@ func (s *testParserSuite) TestBuiltin(c *C) {
|
||||
{`select from_unixtime(1447430881.1234567, "%Y %D %M %h:%i:%s %x")`, true, "SELECT FROM_UNIXTIME(1447430881.1234567, '%Y %D %M %h:%i:%s %x')"},
|
||||
|
||||
// for issue 224
|
||||
{`SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin;`, true, "SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8)"},
|
||||
{`SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin;`, true, "SELECT CAST('test collated returns' AS CHAR CHARSET UTF8)"},
|
||||
|
||||
// for string functions
|
||||
// trim
|
||||
|
||||
@ -252,54 +252,63 @@ func (ft *FieldType) Restore(ctx *format.RestoreCtx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FormatAsCastType is used for write AST back to string.
|
||||
func (ft *FieldType) FormatAsCastType(w io.Writer) {
|
||||
// RestoreAsCastType is used for write AST back to string.
|
||||
func (ft *FieldType) RestoreAsCastType(ctx *format.RestoreCtx) {
|
||||
switch ft.Tp {
|
||||
case mysql.TypeVarString:
|
||||
if ft.Charset == charset.CharsetBin && ft.Collate == charset.CollationBin {
|
||||
fmt.Fprint(w, "BINARY")
|
||||
ctx.WriteKeyWord("BINARY")
|
||||
} else {
|
||||
fmt.Fprint(w, "CHAR")
|
||||
ctx.WriteKeyWord("CHAR")
|
||||
}
|
||||
if ft.Flen != UnspecifiedLength {
|
||||
fmt.Fprintf(w, "(%d)", ft.Flen)
|
||||
ctx.WritePlainf("(%d)", ft.Flen)
|
||||
}
|
||||
if ft.Flag&mysql.BinaryFlag != 0 {
|
||||
fmt.Fprint(w, " BINARY")
|
||||
ctx.WriteKeyWord(" BINARY")
|
||||
}
|
||||
if ft.Charset != charset.CharsetBin && ft.Charset != mysql.DefaultCharset {
|
||||
fmt.Fprintf(w, " CHARACTER SET %s", ft.Charset)
|
||||
ctx.WriteKeyWord(" CHARSET ")
|
||||
ctx.WriteKeyWord(ft.Charset)
|
||||
}
|
||||
case mysql.TypeDate:
|
||||
fmt.Fprint(w, "DATE")
|
||||
ctx.WriteKeyWord("DATE")
|
||||
case mysql.TypeDatetime:
|
||||
fmt.Fprint(w, "DATETIME")
|
||||
ctx.WriteKeyWord("DATETIME")
|
||||
if ft.Decimal > 0 {
|
||||
fmt.Fprintf(w, "(%d)", ft.Decimal)
|
||||
ctx.WritePlainf("(%d)", ft.Decimal)
|
||||
}
|
||||
case mysql.TypeNewDecimal:
|
||||
fmt.Fprint(w, "DECIMAL")
|
||||
ctx.WriteKeyWord("DECIMAL")
|
||||
if ft.Flen > 0 && ft.Decimal > 0 {
|
||||
fmt.Fprintf(w, "(%d, %d)", ft.Flen, ft.Decimal)
|
||||
ctx.WritePlainf("(%d, %d)", ft.Flen, ft.Decimal)
|
||||
} else if ft.Flen > 0 {
|
||||
fmt.Fprintf(w, "(%d)", ft.Flen)
|
||||
ctx.WritePlainf("(%d)", ft.Flen)
|
||||
}
|
||||
case mysql.TypeDuration:
|
||||
fmt.Fprint(w, "TIME")
|
||||
ctx.WriteKeyWord("TIME")
|
||||
if ft.Decimal > 0 {
|
||||
fmt.Fprintf(w, "(%d)", ft.Decimal)
|
||||
ctx.WritePlainf("(%d)", ft.Decimal)
|
||||
}
|
||||
case mysql.TypeLonglong:
|
||||
if ft.Flag&mysql.UnsignedFlag != 0 {
|
||||
fmt.Fprint(w, "UNSIGNED")
|
||||
ctx.WriteKeyWord("UNSIGNED")
|
||||
} else {
|
||||
fmt.Fprint(w, "SIGNED")
|
||||
ctx.WriteKeyWord("SIGNED")
|
||||
}
|
||||
case mysql.TypeJSON:
|
||||
fmt.Fprint(w, "JSON")
|
||||
ctx.WriteKeyWord("JSON")
|
||||
}
|
||||
}
|
||||
|
||||
// FormatAsCastType is used for write AST back to string.
|
||||
func (ft *FieldType) FormatAsCastType(w io.Writer) {
|
||||
var sb strings.Builder
|
||||
restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)
|
||||
ft.RestoreAsCastType(restoreCtx)
|
||||
fmt.Fprint(w, sb.String())
|
||||
}
|
||||
|
||||
// VarStorageLen indicates this column is a variable length column.
|
||||
const VarStorageLen = -1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user