Skip to content

Commit

Permalink
sqlparser: Track if original default value is a literal
Browse files Browse the repository at this point in the history
We want to keep if the user explicitly specified an expression or
something we treat as a literal, so we can serialize it back to the
exact same format.

This is because for example `now()` and `(now())` do have a semantic
difference. The latter works as a default for `datetime(6)` with a
precision, while the former does not and would need `now(6)` in the
literal form.

Signed-off-by: Dirkjan Bussink <d.bussink@gmail.com>
  • Loading branch information
dbussink committed Aug 6, 2023
1 parent 4d9d94c commit 02e25e2
Show file tree
Hide file tree
Showing 12 changed files with 2,662 additions and 2,674 deletions.
2 changes: 1 addition & 1 deletion go/test/endtoend/vtgate/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ ts12 TIMESTAMP DEFAULT LOCALTIME()
)`)
utils.Exec(t, conn, "drop table function_default")

utils.Exec(t, conn, `create table function_default (ts TIMESTAMP DEFAULT UTC_TIMESTAMP)`)
utils.Exec(t, conn, `create table function_default (ts TIMESTAMP DEFAULT (UTC_TIMESTAMP))`)
utils.Exec(t, conn, "drop table function_default")

utils.Exec(t, conn, `create table function_default (x varchar(25) DEFAULT "check")`)
Expand Down
4 changes: 2 additions & 2 deletions go/test/endtoend/vtgate/mysql80/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ ts12 TIMESTAMP DEFAULT LOCALTIME()
)`)
utils.Exec(t, conn, "drop table function_default")

// this query works because utc_timestamp will get parenthesised before reaching MySQL. However, this syntax is not supported in MySQL 8.0
utils.Exec(t, conn, `create table function_default (ts TIMESTAMP DEFAULT UTC_TIMESTAMP)`)
// this query works only as an expression.
utils.Exec(t, conn, `create table function_default (ts TIMESTAMP DEFAULT (UTC_TIMESTAMP))`)
utils.Exec(t, conn, "drop table function_default")

utils.Exec(t, conn, `create table function_default (x varchar(25) DEFAULT "check")`)
Expand Down
4 changes: 4 additions & 0 deletions go/vt/schemadiff/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ func (c *CreateTableEntity) normalizeColumnOptions() {
// See also https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
if _, ok := col.Type.Options.Default.(*sqlparser.NullVal); ok {
col.Type.Options.Default = nil
col.Type.Options.DefaultLiteral = false
}
}

Expand Down Expand Up @@ -507,6 +508,7 @@ func (c *CreateTableEntity) normalizeColumnOptions() {
Type: sqlparser.StrVal,
Val: defaultVal,
}
col.Type.Options.DefaultLiteral = true
} else {
col.Type.Options.Default = nil
}
Expand Down Expand Up @@ -2046,8 +2048,10 @@ func (c *CreateTableEntity) apply(diff *AlterTableEntityDiff) error {
found = true
if opt.DropDefault {
col.Type.Options.Default = nil
col.Type.Options.DefaultLiteral = false
} else if opt.DefaultVal != nil {
col.Type.Options.Default = opt.DefaultVal
col.Type.Options.DefaultLiteral = opt.DefaultLiteral
}
col.Type.Options.Invisible = opt.Invisible
break
Expand Down
26 changes: 14 additions & 12 deletions go/vt/sqlparser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,11 @@ type (

// AlterColumn is used to add or drop defaults & visibility to columns in alter table command
AlterColumn struct {
Column *ColName
DropDefault bool
DefaultVal Expr
Invisible *bool
Column *ColName
DropDefault bool
DefaultVal Expr
DefaultLiteral bool
Invisible *bool
}

// With contains the lists of common table expression and specifies if it is recursive or not
Expand Down Expand Up @@ -1834,14 +1835,15 @@ type ColumnTypeOptions struct {
The complexity arises from the fact that we do not know whether the column will be nullable or not if nothing is specified.
Therefore we do not know whether the column is nullable or not in case 3.
*/
Null *bool
Autoincrement bool
Default Expr
OnUpdate Expr
As Expr
Comment *Literal
Storage ColumnStorage
Collate string
Null *bool
Autoincrement bool
Default Expr
DefaultLiteral bool
OnUpdate Expr
As Expr
Comment *Literal
Storage ColumnStorage
Collate string
// Reference stores a foreign key constraint for the given column
Reference *ReferenceDefinition

Expand Down
2 changes: 2 additions & 0 deletions go/vt/sqlparser/ast_equals.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 8 additions & 4 deletions go/vt/sqlparser/ast_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -717,10 +717,10 @@ func (ct *ColumnType) Format(buf *TrackedBuffer) {
}
if ct.Options.Default != nil {
buf.astPrintf(ct, " %s", keywordStrings[DEFAULT])
if defaultRequiresParens(ct) {
buf.astPrintf(ct, " (%v)", ct.Options.Default)
} else {
if ct.Options.DefaultLiteral {
buf.astPrintf(ct, " %v", ct.Options.Default)
} else {
buf.astPrintf(ct, " (%v)", ct.Options.Default)
}
}
if ct.Options.OnUpdate != nil {
Expand Down Expand Up @@ -2250,7 +2250,11 @@ func (node *AlterColumn) Format(buf *TrackedBuffer) {
if node.DropDefault {
buf.astPrintf(node, " drop default")
} else if node.DefaultVal != nil {
buf.astPrintf(node, " set default %v", node.DefaultVal)
if node.DefaultLiteral {
buf.astPrintf(node, " set default %v", node.DefaultVal)
} else {
buf.astPrintf(node, " set default (%v)", node.DefaultVal)
}
}
if node.Invisible != nil {
if *node.Invisible {
Expand Down
18 changes: 12 additions & 6 deletions go/vt/sqlparser/ast_format_fast.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 0 additions & 22 deletions go/vt/sqlparser/ast_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2171,28 +2171,6 @@ func isExprLiteral(expr Expr) bool {
}
}

func defaultRequiresParens(ct *ColumnType) bool {
// in 5.7 null value should be without parenthesis, in 8.0 it is allowed either way.
// so it is safe to not keep parenthesis around null.
if _, isNullVal := ct.Options.Default.(*NullVal); isNullVal {
return false
}

switch strings.ToUpper(ct.Type) {
case "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "TINYBLOB", "BLOB", "MEDIUMBLOB",
"LONGBLOB", "JSON", "GEOMETRY", "POINT",
"LINESTRING", "POLYGON", "MULTIPOINT", "MULTILINESTRING",
"MULTIPOLYGON", "GEOMETRYCOLLECTION":
return true
}

if isExprLiteral(ct.Options.Default) || isExprAliasForCurrentTimeStamp(ct.Options.Default) {
return false
}

return true
}

// RemoveKeyspaceFromColName removes the Qualifier.Qualifier on all ColNames in the expression tree
func RemoveKeyspaceFromColName(expr Expr) {
RemoveKeyspace(expr)
Expand Down
36 changes: 21 additions & 15 deletions go/vt/sqlparser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,19 @@ var (
output: "create table x (\n\tlocation GEOMETRYCOLLECTION default (point(7.0, 3.0))\n)",
}, {
input: "create table t (id int primary key, dt datetime DEFAULT (CURRENT_TIMESTAMP))",
output: "create table t (\n\tid int primary key,\n\tdt datetime default current_timestamp()\n)",
output: "create table t (\n\tid int primary key,\n\tdt datetime default (current_timestamp())\n)",
}, {
input: "create table t (id int primary key, dt datetime DEFAULT now())",
output: "create table t (\n\tid int primary key,\n\tdt datetime default now()\n)",
}, {
input: "create table t (id int primary key, dt datetime DEFAULT (now()))",
output: "create table t (\n\tid int primary key,\n\tdt datetime default now()\n)",
output: "create table t (\n\tid int primary key,\n\tdt datetime default (now())\n)",
}, {
input: "create table t (id int primary key, dt datetime(6) DEFAULT (now()))",
output: "create table t (\n\tid int primary key,\n\tdt datetime(6) default (now())\n)",
}, {
input: "create table t (id int primary key, dt datetime DEFAULT (now() + 1))",
output: "create table t (\n\tid int primary key,\n\tdt datetime default (now() + 1)\n)",
}, {
input: "create table x (e enum('red','yellow') null collate 'utf8_bin')",
output: "create table x (\n\te enum('red', 'yellow') collate 'utf8_bin' null\n)",
Expand Down Expand Up @@ -93,52 +99,52 @@ var (
output: "select extract(microsecond from '2003-01-02 10:30:00.000123') from dual",
}, {
input: "CREATE TABLE t2 (b BLOB DEFAULT 'abc')",
output: "create table t2 (\n\tb BLOB default ('abc')\n)",
output: "create table t2 (\n\tb BLOB default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b blob DEFAULT 'abc')",
output: "create table t2 (\n\tb blob default ('abc')\n)",
output: "create table t2 (\n\tb blob default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b BLOB DEFAULT ('abc'))",
output: "create table t2 (\n\tb BLOB default ('abc')\n)",
}, {
input: "CREATE TABLE t2 (b TINYBLOB DEFAULT 'abc')",
output: "create table t2 (\n\tb TINYBLOB default ('abc')\n)",
output: "create table t2 (\n\tb TINYBLOB default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b TINYBLOB DEFAULT ('abc'))",
output: "create table t2 (\n\tb TINYBLOB default ('abc')\n)",
}, {
input: "CREATE TABLE t2 (b MEDIUMBLOB DEFAULT 'abc')",
output: "create table t2 (\n\tb MEDIUMBLOB default ('abc')\n)",
output: "create table t2 (\n\tb MEDIUMBLOB default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b MEDIUMBLOB DEFAULT ('abc'))",
output: "create table t2 (\n\tb MEDIUMBLOB default ('abc')\n)",
}, {
input: "CREATE TABLE t2 (b LONGBLOB DEFAULT 'abc')",
output: "create table t2 (\n\tb LONGBLOB default ('abc')\n)",
output: "create table t2 (\n\tb LONGBLOB default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b LONGBLOB DEFAULT ('abc'))",
output: "create table t2 (\n\tb LONGBLOB default ('abc')\n)",
}, {
input: "CREATE TABLE t2 (b TEXT DEFAULT 'abc')",
output: "create table t2 (\n\tb TEXT default ('abc')\n)",
output: "create table t2 (\n\tb TEXT default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b TEXT DEFAULT ('abc'))",
output: "create table t2 (\n\tb TEXT default ('abc')\n)",
}, {
input: "CREATE TABLE t2 (b TINYTEXT DEFAULT 'abc')",
output: "create table t2 (\n\tb TINYTEXT default ('abc')\n)",
output: "create table t2 (\n\tb TINYTEXT default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b TINYTEXT DEFAULT ('abc'))",
output: "create table t2 (\n\tb TINYTEXT default ('abc')\n)",
}, {
input: "CREATE TABLE t2 (b MEDIUMTEXT DEFAULT 'abc')",
output: "create table t2 (\n\tb MEDIUMTEXT default ('abc')\n)",
output: "create table t2 (\n\tb MEDIUMTEXT default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b MEDIUMTEXT DEFAULT ('abc'))",
output: "create table t2 (\n\tb MEDIUMTEXT default ('abc')\n)",
}, {
input: "CREATE TABLE t2 (b LONGTEXT DEFAULT 'abc')",
output: "create table t2 (\n\tb LONGTEXT default ('abc')\n)",
output: "create table t2 (\n\tb LONGTEXT default 'abc'\n)",
}, {
input: "CREATE TABLE t2 (b LONGTEXT DEFAULT ('abc'))",
output: "create table t2 (\n\tb LONGTEXT default ('abc')\n)",
Expand All @@ -147,16 +153,16 @@ var (
output: "create table t2 (\n\tb JSON default null\n)",
}, {
input: "CREATE TABLE t2 (b JSON DEFAULT (null))",
output: "create table t2 (\n\tb JSON default null\n)",
output: "create table t2 (\n\tb JSON default (null)\n)",
}, {
input: "CREATE TABLE t2 (b JSON DEFAULT '{name:abc}')",
output: "create table t2 (\n\tb JSON default ('{name:abc}')\n)",
output: "create table t2 (\n\tb JSON default '{name:abc}'\n)",
}, {
input: "CREATE TABLE t2 (b JSON DEFAULT ('{name:abc}'))",
output: "create table t2 (\n\tb JSON default ('{name:abc}')\n)",
}, {
input: "create table x(location POINT DEFAULT 7.0)",
output: "create table x (\n\tlocation POINT default (7.0)\n)",
output: "create table x (\n\tlocation POINT default 7.0\n)",
}, {
input: "create table x(location POINT DEFAULT (7.0))",
output: "create table x (\n\tlocation POINT default (7.0)\n)",
Expand Down Expand Up @@ -5001,7 +5007,7 @@ func TestCreateTable(t *testing.T) {
output: `create table t (
time1 timestamp default now(),
time2 timestamp default now(),
time3 timestamp default now(),
time3 timestamp default (now()),
time4 timestamp default now() on update now(),
time5 timestamp default now() on update now(),
time6 timestamp(3) default now(3) on update now(3)
Expand Down
Loading

0 comments on commit 02e25e2

Please sign in to comment.