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:In non-strict sql mode, fix column type is text /blob/json default value null not processed. #7230

Merged
merged 11 commits into from
Aug 6, 2018

Conversation

ciscoxll
Copy link
Contributor

@ciscoxll ciscoxll commented Aug 1, 2018

What have you changed? (mandatory)

  • In non-strict SQL mode, we can ignore that the default value of a text /blob column is an empty string.
  • In non-strict SQL mode, if the column type is json and the default value is null, it is initialized to an empty array.
  • Fix issue default value for text/blob/json type #7158

master branch

mysql> create table test_text_default_value(c1 text not null default '');
ERROR 1050 (42S01): Table 'test.test_text_default_value' already exists

mysql> create table test_text_default_json(c1 json not null default '');
ERROR 1101 (42000): BLOB/TEXT/JSON column 'c1' can't have a default value

mysql> create table test_text_default_blob(c1 blob not null default '');
ERROR 1101 (42000): BLOB/TEXT/JSON column 'c1' can't have a default value
mysql> set sql_mode='';
Query OK, 0 rows affected (0.00 sec)

mysql>  create table test_text_default_value(c1 text not null default '');
ERROR 1101 (42000): BLOB/TEXT/JSON column 'c1' can't have a default value

mysql> create table test_text_default_json(c1 json not null default '');
ERROR 1101 (42000): BLOB/TEXT/JSON column 'c1' can't have a default value
mysql> create table test_text_default_blob(c1 blob not null default '');

ERROR 1101 (42000): BLOB/TEXT/JSON column 'c1' can't have a default value

sql-mode-compatible branch

tidb> create table test_text_default_value(c1 text not null default '');
ERROR 1101 (42000): BLOB, TEXT, GEOMETRY or JSON column 'c1' can't have a default value
tidbl> set sql_mode='';
Query OK, 0 rows affected (0.00 sec)

tidb> create table test_text_default_json(c1 json not null default '');
Query OK, 0 rows affected, 1 warning (0.03 sec)

tidb> show create table test_text_default_json;
+------------------------+--------------------------------------------------------------------------------------------------------------------+
| Table                  | Create Table                                                                                                       |
+------------------------+--------------------------------------------------------------------------------------------------------------------+
| test_text_default_json | CREATE TABLE `test_text_default_json` (
  `c1` json NOT NULL DEFAULT 'null'
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+------------------------+--------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
tidb> create table test_text_default_value(c1 text not null default '');
ERROR 1101 (42000): BLOB, TEXT, GEOMETRY or JSON column 'c1' can't have a default value
tidb> set sql_mode='';
Query OK, 0 rows affected (0.00 sec)

tidb> create table test_text_default_value(c1 text not null default '');
Query OK, 0 rows affected, 1 warning (0.02 sec)

tidb> show warnings;
+---------+------+---------------------------------------------------------------------+
| Level   | Code | Message                                                             |
+---------+------+---------------------------------------------------------------------+
| Warning | 1101 | BLOB, TEXT, GEOMETRY or JSON column 'c1' can't have a default value |
+---------+------+---------------------------------------------------------------------+
1 row in set (0.01 sec)

tidb> show create table test_text_default_value;
+-------------------------+------------------------------------------------------------------------------------------------------+
| Table                   | Create Table                                                                                         |
+-------------------------+------------------------------------------------------------------------------------------------------+
| test_text_default_value | CREATE TABLE `test_text_default_value` (
  `c1` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------------------------+------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

What is the type of the changes? (mandatory)

Improvement

  • Improvement (non-breaking change which is an improvement to an existing feature)

How has this PR been tested? (mandatory)

Unit tests.

Does this PR affect documentation (docs/docs-cn) update? (mandatory)

No

Does this PR affect tidb-ansible update? (mandatory)

No

Does this PR need to be added to the release notes? (mandatory)

No

@ciscoxll ciscoxll changed the title ddl:In non-strict SQL mode, fix column type is text /blob/json default value null not processed. ddl:In non-strict sql mode, fix column type is text /blob/json default value null not processed. Aug 1, 2018
ddl/ddl_api.go Outdated
@@ -244,10 +244,15 @@ func buildColumnAndConstraint(ctx sessionctx.Context, offset int,

// checkColumnCantHaveDefaultValue checks the column can have value as default or not.
// Now, TEXT/BLOB/JSON can't have not null value as default.
func checkColumnCantHaveDefaultValue(col *table.Column, value interface{}) (err error) {
func checkColumnCantHaveDefaultValue(ctx sessionctx.Context, col *table.Column, value interface{}) (err error) {
Copy link
Contributor

@crazycs520 crazycs520 Aug 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about this?

func checkColumnCantHaveDefaultValue(ctx sessionctx.Context, col *table.Column, value interface{}) (error, bool, interface{}) {
	if value != nil && (col.Tp == mysql.TypeJSON ||
		col.Tp == mysql.TypeTinyBlob || col.Tp == mysql.TypeMediumBlob ||
		col.Tp == mysql.TypeLongBlob || col.Tp == mysql.TypeBlob) {
		if !ctx.GetSessionVars().SQLMode.HasStrictMode() && value == "" {
			sc := ctx.GetSessionVars().StmtCtx
			sc.AppendWarning(errBlobCantHaveDefault.GenByArgs(col.Name.O))
			if col.Tp == mysql.TypeJSON {
				return nil, true, `null`
			}
			return nil, false, nil
		}
		// TEXT/BLOB/JSON can't have not null default values.
		return errBlobCantHaveDefault.GenByArgs(col.Name.O), false, nil
	}
	return nil, true, value
}

and in line 322:

				err, hasDefaultValue, value  = checkColumnCantHaveDefaultValue(ctx, col, value);
				if err != nil {
					return nil, nil, errors.Trace(err)
				}
				if hasDefaultValue {
					col.DefaultValue = value
					removeOnUpdateNowFlag(col)
				}

and in line 1443

			err, hasDefaultValue, value = checkColumnCantHaveDefaultValue(ctx, col, value)
			if err != nil {
				return errors.Trace(err)
			}
			if hasDefaultValue {
				col.DefaultValue = value
			}

then we can remove ignoreNullDefaultValuesForColumns function

is := domain.GetDomain(ctx).InfoSchema()
tblInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("text_default_text"))
c.Assert(err, IsNil)
c.Assert(tblInfo.Meta().Columns[0].DefaultValue, Equals, "")
Copy link
Contributor

@crazycs520 crazycs520 Aug 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the DefaultValue should be nil, not be ""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultValue should be ""

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the default value of the column is null, DefaultValue is "" .

ddl/db_test.go Outdated
is = domain.GetDomain(ctx).InfoSchema()
tblInfo, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("text_default_blob"))
c.Assert(err, IsNil)
c.Assert(tblInfo.Meta().Columns[0].DefaultValue, Equals, "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have explained.

ddl/db_test.go Outdated
is = domain.GetDomain(ctx).InfoSchema()
tblInfo, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("text_default_json"))
c.Assert(err, IsNil)
c.Assert(tblInfo.Meta().Columns[0].DefaultValue, Equals, `null`)
Copy link
Contributor

@crazycs520 crazycs520 Aug 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto
I got it, It should be null....

ddl/ddl_api.go Outdated
// In non-strict SQL mode, if the column type is json and the default value is null, it is initialized to an empty array.
if col.Tp == mysql.TypeJSON && value == "" {
col.DefaultValue = `null`
hasDefaultValue = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line can be remove

ddl/ddl_api.go Outdated
if value != nil && (col.Tp == mysql.TypeJSON ||
col.Tp == mysql.TypeTinyBlob || col.Tp == mysql.TypeMediumBlob ||
col.Tp == mysql.TypeLongBlob || col.Tp == mysql.TypeBlob) {
if !ctx.GetSessionVars().SQLMode.HasStrictMode() && value == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will happen when SQL mode change, create a table with SQLMode A and use it wit SQLMode B ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In non-strict SQL mode, if the default value of the column is an empty string, can ignore it, the table can be created successfully.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand.

@tiancaiamao
Copy link
Contributor

/run-all-tests


s.tk.MustExec("set sql_mode='';")
s.tk.MustExec("drop table if exists text_default_text;")
s.tk.MustExec("create table text_default_text(c1 text not null default '');")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test about show create table text_default_text and show create table test_text_default_value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winkyao Done.

Copy link
Contributor

@crazycs520 crazycs520 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ciscoxll ciscoxll added the status/LGT1 Indicates that a PR has LGTM 1. label Aug 2, 2018
@ciscoxll
Copy link
Contributor Author

ciscoxll commented Aug 2, 2018

/rebuild

@ciscoxll
Copy link
Contributor Author

ciscoxll commented Aug 2, 2018

/run-all-tests

@ciscoxll
Copy link
Contributor Author

ciscoxll commented Aug 2, 2018

@tiancaiamao @winkyao PTAL.

ddl/ddl_api.go Outdated
if value != nil && (col.Tp == mysql.TypeJSON ||
col.Tp == mysql.TypeTinyBlob || col.Tp == mysql.TypeMediumBlob ||
col.Tp == mysql.TypeLongBlob || col.Tp == mysql.TypeBlob) {
if !ctx.GetSessionVars().SQLMode.HasStrictMode() && value == "" {
// In non-strict SQL mode, if the default value of the column is an empty string, can ignore it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/can ignore it/the default value can be ignored

ddl/ddl_api.go Outdated
if value != nil && (col.Tp == mysql.TypeJSON ||
col.Tp == mysql.TypeTinyBlob || col.Tp == mysql.TypeMediumBlob ||
col.Tp == mysql.TypeLongBlob || col.Tp == mysql.TypeBlob) {
if !ctx.GetSessionVars().SQLMode.HasStrictMode() && value == "" {
// In non-strict SQL mode, if the default value of the column is an empty string, can ignore it.
if col.Tp == mysql.TypeBlob || col.Tp == mysql.TypeLongBlob && value == "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&& value == "" is redundant because it has been tested at line 261

ddl/ddl_api.go Outdated
@@ -253,14 +253,24 @@ func buildColumnAndConstraint(ctx sessionctx.Context, offset int,

// checkColumnCantHaveDefaultValue checks the column can have value as default or not.
// Now, TEXT/BLOB/JSON can't have not null value as default.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update comment... in strict mode, TEXT/BLOB/JSON can't have not null value as default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, this function signature is very strange:
why checkColumnCantHaveDefaultValue return a boolean value hasDefaultValue

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do agree with tiancaiamao.

ddl/db_test.go Outdated
s.tk.MustExec("drop table if exists text_default_text;")
s.tk.MustExec("create table text_default_text(c1 text not null default '');")
s.tk.MustQuery(`show create table text_default_text`).Check(testutil.RowsWithSep("|",
""+
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this line?

ddl/ddl_api.go Outdated
}
sc := ctx.GetSessionVars().StmtCtx
sc.AppendWarning(errBlobCantHaveDefault.GenByArgs(col.Name.O))
return hasDefaultValue, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can return a specific error here, then remove hasDefaultValue. You can test the specific error in the caller to set the hasDefaultValue.

@ciscoxll
Copy link
Contributor Author

ciscoxll commented Aug 3, 2018

@tiancaiamao @winkyao PTAL.

ddl/ddl_api.go Outdated
sc.AppendWarning(errBlobCantHaveDefault.GenByArgs(col.Name.O))
return hasDefaultValue, value, nil
}
// In strict SQL mode.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In strict SQL mode or default value is not an empty string.

Copy link
Contributor

@winkyao winkyao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reset LGTM

@ciscoxll ciscoxll added status/LGT2 Indicates that a PR has LGTM 2. and removed status/LGT1 Indicates that a PR has LGTM 1. labels Aug 6, 2018
@tiancaiamao
Copy link
Contributor

LGTM

Copy link
Contributor

@winkyao winkyao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ciscoxll ciscoxll merged commit d261b6a into pingcap:master Aug 6, 2018
@ciscoxll ciscoxll deleted the sql-mode-compatible branch August 6, 2018 03:20
@sre-bot sre-bot added the contribution This PR is from a community contributor. label Dec 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contribution This PR is from a community contributor. status/LGT2 Indicates that a PR has LGTM 2. type/compatibility
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants