Skip to content

Commit

Permalink
types: fix creating partition tables fail in ANSI_QUOTES mode (#35379)
Browse files Browse the repository at this point in the history
ref #24980, close #35281
  • Loading branch information
djshow832 authored Jun 21, 2022
1 parent fcfa26c commit 0e7f883
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 16 deletions.
74 changes: 67 additions & 7 deletions ddl/db_partition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1283,15 +1283,15 @@ func TestAlterTableAddPartitionByListColumns(t *testing.T) {
require.Equal(t, "id", part.Columns[0].O)
require.Equal(t, "name", part.Columns[1].O)
require.Len(t, part.Definitions, 5)
require.Equal(t, [][]string{{"1", `"a"`}, {"2", `"b"`}}, part.Definitions[0].InValues)
require.Equal(t, [][]string{{"1", `'a'`}, {"2", `'b'`}}, part.Definitions[0].InValues)
require.Equal(t, model.NewCIStr("p0"), part.Definitions[0].Name)
require.Equal(t, [][]string{{"3", `"a"`}, {"4", `"b"`}}, part.Definitions[1].InValues)
require.Equal(t, [][]string{{"3", `'a'`}, {"4", `'b'`}}, part.Definitions[1].InValues)
require.Equal(t, model.NewCIStr("p1"), part.Definitions[1].Name)
require.Equal(t, [][]string{{"5", `NULL`}}, part.Definitions[2].InValues)
require.Equal(t, model.NewCIStr("p3"), part.Definitions[2].Name)
require.Equal(t, [][]string{{"7", `"a"`}}, part.Definitions[3].InValues)
require.Equal(t, [][]string{{"7", `'a'`}}, part.Definitions[3].InValues)
require.Equal(t, model.NewCIStr("p4"), part.Definitions[3].Name)
require.Equal(t, [][]string{{"8", `"a"`}}, part.Definitions[4].InValues)
require.Equal(t, [][]string{{"8", `'a'`}}, part.Definitions[4].InValues)
require.Equal(t, model.NewCIStr("p5"), part.Definitions[4].Name)

errorCases := []struct {
Expand Down Expand Up @@ -1387,9 +1387,9 @@ func TestAlterTableDropPartitionByListColumns(t *testing.T) {
require.Equal(t, "id", part.Columns[0].O)
require.Equal(t, "name", part.Columns[1].O)
require.Len(t, part.Definitions, 2)
require.Equal(t, [][]string{{"1", `"a"`}, {"2", `"b"`}}, part.Definitions[0].InValues)
require.Equal(t, [][]string{{"1", `'a'`}, {"2", `'b'`}}, part.Definitions[0].InValues)
require.Equal(t, model.NewCIStr("p0"), part.Definitions[0].Name)
require.Equal(t, [][]string{{"5", `"a"`}, {"NULL", "NULL"}}, part.Definitions[1].InValues)
require.Equal(t, [][]string{{"5", `'a'`}, {"NULL", "NULL"}}, part.Definitions[1].InValues)
require.Equal(t, model.NewCIStr("p3"), part.Definitions[1].Name)

sql := "alter table t drop partition p10;"
Expand Down Expand Up @@ -1454,7 +1454,7 @@ func TestAlterTableTruncatePartitionByListColumns(t *testing.T) {
part := tbl.Meta().Partition
require.True(t, part.Type == model.PartitionTypeList)
require.Len(t, part.Definitions, 3)
require.Equal(t, [][]string{{"3", `"a"`}, {"4", `"b"`}}, part.Definitions[1].InValues)
require.Equal(t, [][]string{{"3", `'a'`}, {"4", `'b'`}}, part.Definitions[1].InValues)
require.Equal(t, model.NewCIStr("p1"), part.Definitions[1].Name)
require.False(t, part.Definitions[1].ID == oldTbl.Meta().Partition.Definitions[1].ID)

Expand Down Expand Up @@ -3615,3 +3615,63 @@ func TestDuplicatePartitionNames(t *testing.T) {
"(PARTITION `p2` VALUES IN (2),\n" +
" PARTITION `p3` VALUES IN (3))"))
}

func TestPartitionTableWithAnsiQuotes(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()
tk := testkit.NewTestKit(t, store)
tk.MustExec("create database partitionWithAnsiQuotes")
defer tk.MustExec("drop database partitionWithAnsiQuotes")
tk.MustExec("use partitionWithAnsiQuotes")
tk.MustExec("SET SESSION sql_mode='ANSI_QUOTES'")

// Test single quotes.
tk.MustExec(`create table t(created_at datetime) PARTITION BY RANGE COLUMNS(created_at) (
PARTITION p0 VALUES LESS THAN ('2021-12-01 00:00:00'),
PARTITION p1 VALUES LESS THAN ('2022-01-01 00:00:00'))`)
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE \"t\" (\n" +
" \"created_at\" datetime DEFAULT NULL\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" +
"PARTITION BY RANGE COLUMNS(\"created_at\")\n" +
"(PARTITION \"p0\" VALUES LESS THAN ('2021-12-01 00:00:00'),\n" +
" PARTITION \"p1\" VALUES LESS THAN ('2022-01-01 00:00:00'))"))
tk.MustExec("drop table t")

// Test expression with single quotes.
tk.MustExec(`create table t(created_at timestamp) PARTITION BY RANGE (unix_timestamp(created_at)) (
PARTITION p0 VALUES LESS THAN (unix_timestamp('2021-12-01 00:00:00')),
PARTITION p1 VALUES LESS THAN (unix_timestamp('2022-01-01 00:00:00')))`)
// FIXME: should be "created_at" instead of `created_at`, see #35389.
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE \"t\" (\n" +
" \"created_at\" timestamp NULL DEFAULT NULL\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" +
"PARTITION BY RANGE (UNIX_TIMESTAMP(`created_at`))\n" +
"(PARTITION \"p0\" VALUES LESS THAN (1638288000),\n" +
" PARTITION \"p1\" VALUES LESS THAN (1640966400))"))
tk.MustExec("drop table t")

// Test values in.
tk.MustExec(`CREATE TABLE t (a int DEFAULT NULL, b varchar(255) DEFAULT NULL) PARTITION BY LIST COLUMNS(a,b) (
PARTITION p0 VALUES IN ((1,'1'),(2,'2')),
PARTITION p1 VALUES IN ((10,'10'),(11,'11')))`)
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE \"t\" (\n" +
" \"a\" int(11) DEFAULT NULL,\n" +
" \"b\" varchar(255) DEFAULT NULL\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" +
"PARTITION BY LIST COLUMNS(\"a\",\"b\")\n" +
"(PARTITION \"p0\" VALUES IN ((1,'1'),(2,'2')),\n" +
" PARTITION \"p1\" VALUES IN ((10,'10'),(11,'11')))"))
tk.MustExec("drop table t")

// Test escaped characters in single quotes.
tk.MustExec(`CREATE TABLE t (a varchar(255) DEFAULT NULL) PARTITION BY LIST COLUMNS(a) (
PARTITION p0 VALUES IN ('\'','\'\'',''''''''),
PARTITION p1 VALUES IN ('""','\\','\\\'\t\n'))`)
tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE \"t\" (\n" +
" \"a\" varchar(255) DEFAULT NULL\n" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" +
"PARTITION BY LIST COLUMNS(\"a\")\n" +
"(PARTITION \"p0\" VALUES IN ('''','''''',''''''''),\n" +
" PARTITION \"p1\" VALUES IN ('\"\"','\\\\','\\\\''\t\n'))"))
tk.MustExec("drop table t")
}
2 changes: 1 addition & 1 deletion ddl/partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -1657,7 +1657,7 @@ func buildCheckSQLForRangeExprPartition(pi *model.PartitionInfo, index int, sche
}

func trimQuotation(str string) string {
return strings.Trim(str, "\"")
return strings.Trim(str, "'")
}

func buildCheckSQLForRangeColumnsPartition(pi *model.PartitionInfo, index int, schemaName, tableName model.CIStr) (string, []interface{}) {
Expand Down
15 changes: 8 additions & 7 deletions executor/showtest/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,15 +404,16 @@ func TestShowCreateTable(t *testing.T) {
tk.MustExec(`create table t (id int, name varchar(10), unique index idx (id, name)) partition by list columns (id, name) (
partition p0 values in ((3, '1'), (5, '5')),
partition p1 values in ((1, '1')));`)
// The strings are single quoted in MySQL even if sql_mode doesn't contain ANSI_QUOTES.
tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|",
"t CREATE TABLE `t` (\n"+
" `id` int(11) DEFAULT NULL,\n"+
" `name` varchar(10) DEFAULT NULL,\n"+
" UNIQUE KEY `idx` (`id`,`name`)\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+
"PARTITION BY LIST COLUMNS(`id`,`name`)\n"+
"(PARTITION `p0` VALUES IN ((3,\"1\"),(5,\"5\")),\n"+
" PARTITION `p1` VALUES IN ((1,\"1\")))"))
"(PARTITION `p0` VALUES IN ((3,'1'),(5,'5')),\n"+
" PARTITION `p1` VALUES IN ((1,'1')))"))
tk.MustExec(`DROP TABLE IF EXISTS t`)
tk.MustExec(`create table t (id int primary key, v varchar(255) not null, key idx_v (v) comment 'foo\'bar')`)
tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|",
Expand Down Expand Up @@ -525,8 +526,8 @@ func TestShowCreateTablePlacement(t *testing.T) {
" `b` varchar(255) DEFAULT NULL\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+
"PARTITION BY LIST COLUMNS(`b`)\n"+
"(PARTITION `pLow` VALUES IN (\"1\",\"2\",\"3\",\"5\",\"8\") COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+
" PARTITION `pMax` VALUES IN (\"10\",\"11\",\"12\"))",
"(PARTITION `pLow` VALUES IN ('1','2','3','5','8') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+
" PARTITION `pMax` VALUES IN ('10','11','12'))",
))

tk.MustExec(`DROP TABLE IF EXISTS t`)
Expand All @@ -540,8 +541,8 @@ func TestShowCreateTablePlacement(t *testing.T) {
" `b` varchar(255) DEFAULT NULL\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+
"PARTITION BY LIST COLUMNS(`a`,`b`)\n"+
"(PARTITION `pLow` VALUES IN ((1,\"1\"),(2,\"2\"),(3,\"3\"),(5,\"5\"),(8,\"8\")) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+
" PARTITION `pMax` VALUES IN ((10,\"10\"),(11,\"11\"),(12,\"12\")))",
"(PARTITION `pLow` VALUES IN ((1,'1'),(2,'2'),(3,'3'),(5,'5'),(8,'8')) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+
" PARTITION `pMax` VALUES IN ((10,'10'),(11,'11'),(12,'12')))",
))

tk.MustExec(`DROP TABLE IF EXISTS t`)
Expand Down Expand Up @@ -570,7 +571,7 @@ func TestShowCreateTablePlacement(t *testing.T) {
" `b` varchar(255) DEFAULT NULL\n"+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+
"PARTITION BY RANGE COLUMNS(`b`)\n"+
"(PARTITION `pLow` VALUES LESS THAN (\"1000000\") COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+
"(PARTITION `pLow` VALUES LESS THAN ('1000000') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+
" PARTITION `pMax` VALUES LESS THAN (MAXVALUE))",
))

Expand Down
6 changes: 5 additions & 1 deletion types/parser_driver/value_expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ func (n *ValueExpr) Format(w io.Writer) {
case types.KindFloat64:
s = strconv.FormatFloat(n.GetFloat64(), 'e', -1, 64)
case types.KindString, types.KindBytes:
s = strconv.Quote(n.GetString())
// If sql_mode='ANSI_QUOTES', strings with double-quotes will be taken as an identifier.
// See #35281.
s = strings.ReplaceAll(n.GetString(), "\\", "\\\\")
s = strings.ReplaceAll(s, `'`, `''`)
s = fmt.Sprintf("'%s'", s)
case types.KindMysqlDecimal:
s = n.GetMysqlDecimal().String()
case types.KindBinaryLiteral:
Expand Down
31 changes: 31 additions & 0 deletions types/parser_driver/value_expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,34 @@ func TestValueExprRestore(t *testing.T) {
})
}
}

func TestValueExprFormat(t *testing.T) {
tests := []struct {
datum types.Datum
expect string
}{
{types.NewDatum(nil), "NULL"},
{types.NewIntDatum(1), "1"},
{types.NewIntDatum(-1), "-1"},
{types.NewUintDatum(1), "1"},
{types.NewFloat32Datum(1.1), "1.1e+00"},
{types.NewFloat64Datum(1.1), "1.1e+00"},
{types.NewStringDatum("test `s't\"r."), "'test `s''t\"r.'"},
{types.NewBytesDatum([]byte("test `s't\"r.")), "'test `s''t\"r.'"},
{types.NewBinaryLiteralDatum([]byte("test `s't\"r.")), "b'11101000110010101110011011101000010000001100000011100110010011101110100001000100111001000101110'"},
{types.NewDecimalDatum(types.NewDecFromInt(321)), "321"},
{types.NewStringDatum("\\"), "'\\\\'"},
{types.NewStringDatum("''"), "''''''"},
{types.NewStringDatum("\\''\t\n"), "'\\\\''''\t\n'"},
}

for _, test := range tests {
test := test
t.Run(test.expect, func(t *testing.T) {
var sb strings.Builder
expr := &ValueExpr{Datum: test.datum}
expr.Format(&sb)
require.Equalf(t, test.expect, sb.String(), "datum: %#v", test.datum)
})
}
}

0 comments on commit 0e7f883

Please sign in to comment.