vitess.io/vitess@v0.16.2/go/vt/schemadiff/table_test.go (about)

     1  /*
     2  Copyright 2022 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package schemadiff
    18  
    19  import (
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"vitess.io/vitess/go/vt/sqlparser"
    27  )
    28  
    29  func TestCreateTableDiff(t *testing.T) {
    30  	tt := []struct {
    31  		name       string
    32  		from       string
    33  		to         string
    34  		fromName   string
    35  		toName     string
    36  		diff       string
    37  		diffs      []string
    38  		cdiff      string
    39  		cdiffs     []string
    40  		isError    bool
    41  		errorMsg   string
    42  		autoinc    int
    43  		rotation   int
    44  		fulltext   int
    45  		colrename  int
    46  		constraint int
    47  		charset    int
    48  		algorithm  int
    49  	}{
    50  		{
    51  			name: "identical",
    52  			from: "create table t (id int primary key)",
    53  			to:   "create table t (id int primary key)",
    54  		},
    55  		{
    56  			name: "identical 2",
    57  			from: "create table t (id int, primary key(id))",
    58  			to:   "create table t (id int, primary key(id))",
    59  		},
    60  		{
    61  			name: "identical, spacing",
    62  			from: "create   table     t    (id int   primary  key)",
    63  			to: `create table t (
    64  						id int primary key
    65  					)`,
    66  		},
    67  		{
    68  			name:  "column case change",
    69  			from:  "create table t (id int not null, PRIMARY KEY(id))",
    70  			to:    "create table t (Id int not null, primary key(id))",
    71  			diff:  "alter table t modify column Id int not null",
    72  			cdiff: "ALTER TABLE `t` MODIFY COLUMN `Id` int NOT NULL",
    73  		},
    74  		{
    75  			name: "identical, name change",
    76  			from: "create table t1 (id int PRIMARY KEY)",
    77  			to:   "create table t2 (id int primary key)",
    78  		},
    79  		{
    80  			name: "identical, case change",
    81  			from: "create table t (id int PRIMARY KEY)",
    82  			to:   "create table t (id int primary key)",
    83  		},
    84  		{
    85  			name: "identical, case change on target",
    86  			from: "create table t (id int primary key)",
    87  			to:   "create table t (id int PRIMARY KEY)",
    88  		},
    89  		{
    90  			name: "identical, case and qualifiers",
    91  			from: "CREATE table `t` (`id` int primary key)",
    92  			to:   "create TABLE t (id int primary key)",
    93  		},
    94  		{
    95  			name: "identical, case and qualifiers 2",
    96  			from: "CREATE table t (`id` int primary key)",
    97  			to:   "create TABLE `t` (id int primary key)",
    98  		},
    99  		{
   100  			name: "identical, case and column qualifiers",
   101  			from: "CREATE table t (`id` int primary key, i int not null default 0)",
   102  			to:   "create TABLE t (id int primary key, `i` int not null default 0)",
   103  		},
   104  		{
   105  			name:  "added column",
   106  			from:  "create table t1 (`id` int primary key)",
   107  			to:    "create table t2 (id int primary key, `i` int not null default 0)",
   108  			diff:  "alter table t1 add column i int not null default 0",
   109  			cdiff: "ALTER TABLE `t1` ADD COLUMN `i` int NOT NULL DEFAULT 0",
   110  		},
   111  		{
   112  			name:     "dropped column",
   113  			from:     "create table t1 (id int primary key, `i` int not null default 0)",
   114  			to:       "create table t2 (`id` int primary key)",
   115  			diff:     "alter table t1 drop column i",
   116  			cdiff:    "ALTER TABLE `t1` DROP COLUMN `i`",
   117  			fromName: "t1",
   118  			toName:   "t2",
   119  		},
   120  		{
   121  			name:  "modified column",
   122  			from:  "create table t1 (id int primary key, `i` int not null default 0)",
   123  			to:    "create table t2 (id int primary key, `i` bigint unsigned default null)",
   124  			diff:  "alter table t1 modify column i bigint unsigned",
   125  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `i` bigint unsigned",
   126  		},
   127  		{
   128  			name:  "added column, dropped column, modified column",
   129  			from:  "create table t1 (id int primary key, `i` int not null default 0, c char(3) default '')",
   130  			to:    "create table t2 (id int primary key, ts timestamp null, `i` bigint unsigned default null)",
   131  			diff:  "alter table t1 drop column c, modify column i bigint unsigned, add column ts timestamp null after id",
   132  			cdiff: "ALTER TABLE `t1` DROP COLUMN `c`, MODIFY COLUMN `i` bigint unsigned, ADD COLUMN `ts` timestamp NULL AFTER `id`",
   133  		},
   134  		// columns, rename
   135  		{
   136  			name:  "rename mid column. consider different",
   137  			from:  "create table t1 (id int primary key, i1 int not null, c char(3) default '')",
   138  			to:    "create table t2 (id int primary key, i2 int not null, c char(3) default '')",
   139  			diff:  "alter table t1 drop column i1, add column i2 int not null after id",
   140  			cdiff: "ALTER TABLE `t1` DROP COLUMN `i1`, ADD COLUMN `i2` int NOT NULL AFTER `id`",
   141  		},
   142  		{
   143  			name:      "rename mid column. statement",
   144  			from:      "create table t1 (id int primary key, i1 int not null, c char(3) default '')",
   145  			to:        "create table t2 (id int primary key, i2 int not null, c char(3) default '')",
   146  			colrename: ColumnRenameHeuristicStatement,
   147  			diff:      "alter table t1 rename column i1 to i2",
   148  			cdiff:     "ALTER TABLE `t1` RENAME COLUMN `i1` TO `i2`",
   149  		},
   150  		{
   151  			name:      "rename last column. statement",
   152  			from:      "create table t1 (id int primary key, i1 int not null)",
   153  			to:        "create table t2 (id int primary key, i2 int not null)",
   154  			colrename: ColumnRenameHeuristicStatement,
   155  			diff:      "alter table t1 rename column i1 to i2",
   156  			cdiff:     "ALTER TABLE `t1` RENAME COLUMN `i1` TO `i2`",
   157  		},
   158  		{
   159  			name:      "rename two columns. statement",
   160  			from:      "create table t1 (id int primary key, i1 int not null, c char(3) default '', v1 varchar(32))",
   161  			to:        "create table t2 (id int primary key, i2 int not null, c char(3) default '', v2 varchar(32))",
   162  			colrename: ColumnRenameHeuristicStatement,
   163  			diff:      "alter table t1 rename column i1 to i2, rename column v1 to v2",
   164  			cdiff:     "ALTER TABLE `t1` RENAME COLUMN `i1` TO `i2`, RENAME COLUMN `v1` TO `v2`",
   165  		},
   166  		{
   167  			name:      "rename mid column and add an index. statement",
   168  			from:      "create table t1 (id int primary key, i1 int not null, c char(3) default '')",
   169  			to:        "create table t2 (id int primary key, i2 int not null, c char(3) default '', key i2_idx(i2))",
   170  			colrename: ColumnRenameHeuristicStatement,
   171  			diff:      "alter table t1 rename column i1 to i2, add key i2_idx (i2)",
   172  			cdiff:     "ALTER TABLE `t1` RENAME COLUMN `i1` TO `i2`, ADD KEY `i2_idx` (`i2`)",
   173  		},
   174  		{
   175  			// in a future iteration, this will generate a RENAME for both column, like in the previous test. Until then, we do not RENAME two successive columns
   176  			name:      "rename two successive columns. statement",
   177  			from:      "create table t1 (id int primary key, i1 int not null, v1 varchar(32))",
   178  			to:        "create table t2 (id int primary key, i2 int not null, v2 varchar(32))",
   179  			colrename: ColumnRenameHeuristicStatement,
   180  			diff:      "alter table t1 drop column i1, drop column v1, add column i2 int not null, add column v2 varchar(32)",
   181  			cdiff:     "ALTER TABLE `t1` DROP COLUMN `i1`, DROP COLUMN `v1`, ADD COLUMN `i2` int NOT NULL, ADD COLUMN `v2` varchar(32)",
   182  		},
   183  		// columns, reordering
   184  		{
   185  			name:  "reorder column",
   186  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   187  			to:    "create table t2 (id int primary key, a int, c int, b int, d int)",
   188  			diff:  "alter table t1 modify column c int after a",
   189  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` int AFTER `a`",
   190  		},
   191  		{
   192  			name:  "reorder column, far jump",
   193  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   194  			to:    "create table t2 (a int, b int, c int, d int, id int primary key)",
   195  			diff:  "alter table t1 modify column id int after d",
   196  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `id` int AFTER `d`",
   197  		},
   198  		{
   199  			name:  "reorder column, far jump with case sentivity",
   200  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   201  			to:    "create table t2 (a int, B int, c int, d int, id int primary key)",
   202  			diff:  "alter table t1 modify column B int, modify column id int after d",
   203  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `B` int, MODIFY COLUMN `id` int AFTER `d`",
   204  		},
   205  		{
   206  			name:  "reorder column, far jump, another reorder",
   207  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   208  			to:    "create table t2 (a int, c int, b int, d int, id int primary key)",
   209  			diff:  "alter table t1 modify column c int after a, modify column id int after d",
   210  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` int AFTER `a`, MODIFY COLUMN `id` int AFTER `d`",
   211  		},
   212  		{
   213  			name:  "reorder column, far jump, another reorder 2",
   214  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   215  			to:    "create table t2 (c int, a int, b int, d int, id int primary key)",
   216  			diff:  "alter table t1 modify column c int first, modify column id int after d",
   217  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` int FIRST, MODIFY COLUMN `id` int AFTER `d`",
   218  		},
   219  		{
   220  			name:  "reorder column, far jump, another reorder 3",
   221  			from:  "create table t1 (id int primary key, a int, b int, c int, d int, e int, f int)",
   222  			to:    "create table t2 (a int, c int, b int, d int, id int primary key, e int, f int)",
   223  			diff:  "alter table t1 modify column c int after a, modify column id int after d",
   224  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` int AFTER `a`, MODIFY COLUMN `id` int AFTER `d`",
   225  		},
   226  		{
   227  			name:  "reorder column, far jump, another reorder, removed columns",
   228  			from:  "create table t1 (id int primary key, a int, b int, c int, d int, e int, f int, g int)",
   229  			to:    "create table t2 (a int, c int, f int, e int, id int primary key, g int)",
   230  			diff:  "alter table t1 drop column b, drop column d, modify column f int after c, modify column id int after e",
   231  			cdiff: "ALTER TABLE `t1` DROP COLUMN `b`, DROP COLUMN `d`, MODIFY COLUMN `f` int AFTER `c`, MODIFY COLUMN `id` int AFTER `e`",
   232  		},
   233  		{
   234  			name:  "two reorders",
   235  			from:  "create table t1 (id int primary key, a int, b int, c int, d int, e int, f int)",
   236  			to:    "create table t2 (id int primary key, b int, a int, c int, e int, d int, f int)",
   237  			diff:  "alter table t1 modify column b int after id, modify column e int after c",
   238  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `b` int AFTER `id`, MODIFY COLUMN `e` int AFTER `c`",
   239  		},
   240  		{
   241  			name:  "two reorders, added and removed columns",
   242  			from:  "create table t1 (id int primary key, a int, b int, c int, d int, e int, f int)",
   243  			to:    "create table t2 (g int, id int primary key, h int, b int, a int, i int, e int, d int, j int, f int, k int)",
   244  			diff:  "alter table t1 drop column c, modify column b int after id, modify column e int after a, add column g int first, add column h int after id, add column i int after a, add column j int after d, add column k int",
   245  			cdiff: "ALTER TABLE `t1` DROP COLUMN `c`, MODIFY COLUMN `b` int AFTER `id`, MODIFY COLUMN `e` int AFTER `a`, ADD COLUMN `g` int FIRST, ADD COLUMN `h` int AFTER `id`, ADD COLUMN `i` int AFTER `a`, ADD COLUMN `j` int AFTER `d`, ADD COLUMN `k` int",
   246  		},
   247  		{
   248  			name:  "reorder column and change data type",
   249  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   250  			to:    "create table t2 (id int primary key, a int, c bigint, b int, d int)",
   251  			diff:  "alter table t1 modify column c bigint after a",
   252  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` bigint AFTER `a`",
   253  		},
   254  		{
   255  			name:  "reorder column, first",
   256  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   257  			to:    "create table t2 (c int, id int primary key, a int, b int, d int)",
   258  			diff:  "alter table t1 modify column c int first",
   259  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` int FIRST",
   260  		},
   261  		{
   262  			name:  "add multiple columns",
   263  			from:  "create table t1 (id int primary key, a int)",
   264  			to:    "create table t2 (id int primary key, a int, b int, c int, d int)",
   265  			diff:  "alter table t1 add column b int, add column c int, add column d int",
   266  			cdiff: "ALTER TABLE `t1` ADD COLUMN `b` int, ADD COLUMN `c` int, ADD COLUMN `d` int",
   267  		},
   268  		{
   269  			name:  "added column in middle",
   270  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   271  			to:    "create table t2 (id int primary key, a int, b int, x int, c int, d int)",
   272  			diff:  "alter table t1 add column x int after b",
   273  			cdiff: "ALTER TABLE `t1` ADD COLUMN `x` int AFTER `b`",
   274  		},
   275  		{
   276  			name:  "added multiple column in middle",
   277  			from:  "create table t1 (id int primary key, a int)",
   278  			to:    "create table t2 (w int, x int, id int primary key, y int, a int, z int)",
   279  			diff:  "alter table t1 add column w int first, add column x int after w, add column y int after id, add column z int",
   280  			cdiff: "ALTER TABLE `t1` ADD COLUMN `w` int FIRST, ADD COLUMN `x` int AFTER `w`, ADD COLUMN `y` int AFTER `id`, ADD COLUMN `z` int",
   281  		},
   282  		{
   283  			name:  "added column first, reorder column",
   284  			from:  "create table t1 (id int primary key, a int)",
   285  			to:    "create table t2 (x int, a int, id int primary key)",
   286  			diff:  "alter table t1 modify column a int first, add column x int first",
   287  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `a` int FIRST, ADD COLUMN `x` int FIRST",
   288  		},
   289  		{
   290  			name:  "added column in middle, add column on end, reorder column",
   291  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   292  			to:    "create table t2 (id int primary key, a int, b int, x int, d int, c int, y int)",
   293  			diff:  "alter table t1 modify column d int after b, add column x int after b, add column y int",
   294  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `d` int AFTER `b`, ADD COLUMN `x` int AFTER `b`, ADD COLUMN `y` int",
   295  		},
   296  		{
   297  			name:  "added column in middle, add column on end, reorder column 2",
   298  			from:  "create table t1 (id int primary key, a int, b int, c int, d int)",
   299  			to:    "create table t2 (id int primary key, a int, c int, x int, b int, d int, y int)",
   300  			diff:  "alter table t1 modify column c int after a, add column x int after c, add column y int",
   301  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `c` int AFTER `a`, ADD COLUMN `x` int AFTER `c`, ADD COLUMN `y` int",
   302  		},
   303  		// keys
   304  		{
   305  			name:  "added key",
   306  			from:  "create table t1 (`id` int primary key, i int)",
   307  			to:    "create table t2 (id int primary key, `i` int, key `i_idx` (i))",
   308  			diff:  "alter table t1 add key i_idx (i)",
   309  			cdiff: "ALTER TABLE `t1` ADD KEY `i_idx` (`i`)",
   310  		},
   311  		{
   312  			name:  "added key without name",
   313  			from:  "create table t1 (`id` int primary key, i int)",
   314  			to:    "create table t2 (id int primary key, `i` int, key (i))",
   315  			diff:  "alter table t1 add key i (i)",
   316  			cdiff: "ALTER TABLE `t1` ADD KEY `i` (`i`)",
   317  		},
   318  		{
   319  			name:  "added key without name, conflicting name",
   320  			from:  "create table t1 (`id` int primary key, i int, key i(i))",
   321  			to:    "create table t2 (id int primary key, `i` int, key i(i), key (i))",
   322  			diff:  "alter table t1 add key i_2 (i)",
   323  			cdiff: "ALTER TABLE `t1` ADD KEY `i_2` (`i`)",
   324  		},
   325  		{
   326  			name:  "added key without name, conflicting name 2",
   327  			from:  "create table t1 (`id` int primary key, i int, key i(i), key i_2(i))",
   328  			to:    "create table t2 (id int primary key, `i` int, key i(i), key i_2(i), key (i))",
   329  			diff:  "alter table t1 add key i_3 (i)",
   330  			cdiff: "ALTER TABLE `t1` ADD KEY `i_3` (`i`)",
   331  		},
   332  		{
   333  			name:  "added column and key",
   334  			from:  "create table t1 (`id` int primary key)",
   335  			to:    "create table t2 (id int primary key, `i` int, key `i_idx` (i))",
   336  			diff:  "alter table t1 add column i int, add key i_idx (i)",
   337  			cdiff: "ALTER TABLE `t1` ADD COLUMN `i` int, ADD KEY `i_idx` (`i`)",
   338  		},
   339  		{
   340  			name:  "modify column primary key",
   341  			from:  "create table t1 (`id` int)",
   342  			to:    "create table t2 (id int primary key)",
   343  			diff:  "alter table t1 add primary key (id)",
   344  			cdiff: "ALTER TABLE `t1` ADD PRIMARY KEY (`id`)",
   345  		},
   346  		{
   347  			name:  "added primary key",
   348  			from:  "create table t1 (`id` int)",
   349  			to:    "create table t2 (id int, primary key(id))",
   350  			diff:  "alter table t1 add primary key (id)",
   351  			cdiff: "ALTER TABLE `t1` ADD PRIMARY KEY (`id`)",
   352  		},
   353  		{
   354  			name:  "dropped primary key",
   355  			from:  "create table t1 (id int, primary key(id))",
   356  			to:    "create table t2 (id int)",
   357  			diff:  "alter table t1 drop primary key",
   358  			cdiff: "ALTER TABLE `t1` DROP PRIMARY KEY",
   359  		},
   360  		{
   361  			name:  "dropped key",
   362  			from:  "create table t1 (`id` int primary key, i int, key i_idx(i))",
   363  			to:    "create table t2 (`id` int primary key, i int)",
   364  			diff:  "alter table t1 drop key i_idx",
   365  			cdiff: "ALTER TABLE `t1` DROP KEY `i_idx`",
   366  		},
   367  		{
   368  			name:  "dropped key 2",
   369  			from:  "create table t1 (`id` int, i int, primary key (id), key i_idx(i))",
   370  			to:    "create table t1 (`id` int, i int, primary key (id))",
   371  			diff:  "alter table t1 drop key i_idx",
   372  			cdiff: "ALTER TABLE `t1` DROP KEY `i_idx`",
   373  		},
   374  		{
   375  			name:  "modified key",
   376  			from:  "create table t1 (`id` int primary key, i int, key i_idx(i))",
   377  			to:    "create table t2 (`id` int primary key, i int, key i_idx(i, id))",
   378  			diff:  "alter table t1 drop key i_idx, add key i_idx (i, id)",
   379  			cdiff: "ALTER TABLE `t1` DROP KEY `i_idx`, ADD KEY `i_idx` (`i`, `id`)",
   380  		},
   381  		{
   382  			name:  "modified primary key",
   383  			from:  "create table t1 (`id` int, i int, primary key(id), key i_idx(i))",
   384  			to:    "create table t2 (`id` int, i int, primary key(id, i),key i_idx(`i`))",
   385  			diff:  "alter table t1 drop primary key, add primary key (id, i)",
   386  			cdiff: "ALTER TABLE `t1` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`, `i`)",
   387  		},
   388  		{
   389  			name: "alternative primary key definition, no diff",
   390  			from: "create table t1 (`id` int primary key, i int)",
   391  			to:   "create table t2 (`id` int, i int, primary key (id))",
   392  		},
   393  		{
   394  			name: "reordered key, no diff",
   395  			from: "create table t1 (`id` int primary key, i int, key i_idx(i), key i2_idx(i, `id`))",
   396  			to:   "create table t2 (`id` int primary key, i int, key i2_idx (`i`, id), key i_idx ( i ) )",
   397  		},
   398  		{
   399  			name: "reordered key, no diff, 2",
   400  			from: "create table t1 (`id` int, i int, primary key(id), key i_idx(i), key i2_idx(i, `id`))",
   401  			to:   "create table t2 (`id` int, i int, key i2_idx (`i`, id), key i_idx ( i ), primary key(id) )",
   402  		},
   403  		{
   404  			name: "reordered key, no diff 3",
   405  			from: "CREATE TABLE `pets` (`id` int, `name` VARCHAR(255), `login` VARCHAR(255), PRIMARY KEY (`id`), KEY (`login`), KEY (`name`) )",
   406  			to:   "CREATE TABLE `pets` (`id` int, `name` VARCHAR(255), `login` VARCHAR(255), PRIMARY KEY (`id`), KEY (`name`), KEY (`login`) )",
   407  		},
   408  		{
   409  			name: "reordered key, no diff 4",
   410  			from: "CREATE TABLE `pets` (`id` int, `name` VARCHAR(255), `login` VARCHAR(255), PRIMARY KEY (`id`), KEY login (login, name), KEY (`login`), KEY (`name`) )",
   411  			to:   "CREATE TABLE `pets` (`id` int, `name` VARCHAR(255), `login` VARCHAR(255), PRIMARY KEY (`id`), KEY (`name`), KEY (`login`), KEY login (login, name) )",
   412  		},
   413  		{
   414  			name:  "reordered key, add key",
   415  			from:  "create table t1 (`id` int primary key, i int, key i_idx(i), key i2_idx(i, `id`))",
   416  			to:    "create table t2 (`id` int primary key, i int, key i2_idx (`i`, id), key i_idx3(id), key i_idx ( i ) )",
   417  			diff:  "alter table t1 add key i_idx3 (id)",
   418  			cdiff: "ALTER TABLE `t1` ADD KEY `i_idx3` (`id`)",
   419  		},
   420  		{
   421  			name:  "key made visible",
   422  			from:  "create table t1 (`id` int primary key, i int, key i_idx(i) invisible)",
   423  			to:    "create table t1 (`id` int primary key, i int, key i_idx(i))",
   424  			diff:  "alter table t1 alter index i_idx visible",
   425  			cdiff: "ALTER TABLE `t1` ALTER INDEX `i_idx` VISIBLE",
   426  		},
   427  		{
   428  			name:  "key made invisible",
   429  			from:  "create table t1 (`id` int primary key, i int, key i_idx(i))",
   430  			to:    "create table t1 (`id` int primary key, i int, key i_idx(i) invisible)",
   431  			diff:  "alter table t1 alter index i_idx invisible",
   432  			cdiff: "ALTER TABLE `t1` ALTER INDEX `i_idx` INVISIBLE",
   433  		},
   434  		{
   435  			name:  "key made invisible with different case",
   436  			from:  "create table t1 (`id` int primary key, i int, key i_idx(i))",
   437  			to:    "create table t1 (`id` int primary key, i int, key i_idx(i) INVISIBLE)",
   438  			diff:  "alter table t1 alter index i_idx invisible",
   439  			cdiff: "ALTER TABLE `t1` ALTER INDEX `i_idx` INVISIBLE",
   440  		},
   441  		// FULLTEXT keys
   442  		{
   443  			name:  "add one fulltext key",
   444  			from:  "create table t1 (id int primary key, name tinytext not null)",
   445  			to:    "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name))",
   446  			diff:  "alter table t1 add fulltext key name_ft (`name`)",
   447  			cdiff: "ALTER TABLE `t1` ADD FULLTEXT KEY `name_ft` (`name`)",
   448  		},
   449  		{
   450  			name:  "add one fulltext key with explicit parser",
   451  			from:  "create table t1 (id int primary key, name tinytext not null)",
   452  			to:    "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) with parser ngram)",
   453  			diff:  "alter table t1 add fulltext key name_ft (`name`) with parser ngram",
   454  			cdiff: "ALTER TABLE `t1` ADD FULLTEXT KEY `name_ft` (`name`) WITH PARSER ngram",
   455  		},
   456  		{
   457  			name:  "add one fulltext key and one normal key",
   458  			from:  "create table t1 (id int primary key, name tinytext not null)",
   459  			to:    "create table t1 (id int primary key, name tinytext not null, key name_idx(name(32)), fulltext key name_ft(name))",
   460  			diff:  "alter table t1 add key name_idx (`name`(32)), add fulltext key name_ft (`name`)",
   461  			cdiff: "ALTER TABLE `t1` ADD KEY `name_idx` (`name`(32)), ADD FULLTEXT KEY `name_ft` (`name`)",
   462  		},
   463  		{
   464  			name:   "add two fulltext keys, distinct statements",
   465  			from:   "create table t1 (id int primary key, name1 tinytext not null, name2 tinytext not null)",
   466  			to:     "create table t1 (id int primary key, name1 tinytext not null, name2 tinytext not null, fulltext key name1_ft(name1), fulltext key name2_ft(name2))",
   467  			diffs:  []string{"alter table t1 add fulltext key name1_ft (name1)", "alter table t1 add fulltext key name2_ft (name2)"},
   468  			cdiffs: []string{"ALTER TABLE `t1` ADD FULLTEXT KEY `name1_ft` (`name1`)", "ALTER TABLE `t1` ADD FULLTEXT KEY `name2_ft` (`name2`)"},
   469  		},
   470  		{
   471  			name:     "add two fulltext keys, unify statements",
   472  			from:     "create table t1 (id int primary key, name1 tinytext not null, name2 tinytext not null)",
   473  			to:       "create table t1 (id int primary key, name1 tinytext not null, name2 tinytext not null, fulltext key name1_ft(name1), fulltext key name2_ft(name2))",
   474  			fulltext: FullTextKeyUnifyStatements,
   475  			diff:     "alter table t1 add fulltext key name1_ft (name1), add fulltext key name2_ft (name2)",
   476  			cdiff:    "ALTER TABLE `t1` ADD FULLTEXT KEY `name1_ft` (`name1`), ADD FULLTEXT KEY `name2_ft` (`name2`)",
   477  		},
   478  		{
   479  			name: "no fulltext diff",
   480  			from: "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) with parser ngram)",
   481  			to:   "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) with parser ngram)",
   482  		},
   483  		{
   484  			name: "no fulltext diff, 2",
   485  			from: "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) with parser ngram)",
   486  			to:   "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) WITH PARSER `ngram`)",
   487  		},
   488  		{
   489  			name: "no fulltext diff, 3",
   490  			from: "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) with parser ngram)",
   491  			to:   "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) /*!50100 WITH PARSER `ngram` */)",
   492  		},
   493  		{
   494  			name: "no fulltext diff",
   495  			from: "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) with parser ngram)",
   496  			to:   "create table t1 (id int primary key, name tinytext not null, fulltext key name_ft(name) with parser NGRAM)",
   497  		},
   498  		// CHECK constraints
   499  		{
   500  			name: "identical check constraints",
   501  			from: "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)))",
   502  			to:   "create table t2 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)))",
   503  			diff: "",
   504  		},
   505  		{
   506  			name:       "check constraints, different name, strict",
   507  			from:       "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)))",
   508  			to:         "create table t2 (id int primary key, i int, constraint `chk_abc123` CHECK ((`i` < 5)))",
   509  			diff:       "alter table t1 drop check check1, add constraint chk_abc123 check (i < 5)",
   510  			cdiff:      "ALTER TABLE `t1` DROP CHECK `check1`, ADD CONSTRAINT `chk_abc123` CHECK (`i` < 5)",
   511  			constraint: ConstraintNamesStrict,
   512  		},
   513  		{
   514  			name:       "check constraints, different name, ignore vitess, non vitess names",
   515  			from:       "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)))",
   516  			to:         "create table t2 (id int primary key, i int, constraint `chk_abc123` CHECK ((`i` < 5)))",
   517  			diff:       "alter table t1 drop check check1, add constraint chk_abc123 check (i < 5)",
   518  			cdiff:      "ALTER TABLE `t1` DROP CHECK `check1`, ADD CONSTRAINT `chk_abc123` CHECK (`i` < 5)",
   519  			constraint: ConstraintNamesIgnoreVitess,
   520  		},
   521  		{
   522  			name:       "check constraints, different name, ignore vitess, vitess names, no match",
   523  			from:       "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)))",
   524  			to:         "create table t2 (id int primary key, i int, constraint `check2_7fp024p4rxvr858tsaggvf9dw` CHECK ((`i` < 5)))",
   525  			diff:       "alter table t1 drop check check1, add constraint check2_7fp024p4rxvr858tsaggvf9dw check (i < 5)",
   526  			cdiff:      "ALTER TABLE `t1` DROP CHECK `check1`, ADD CONSTRAINT `check2_7fp024p4rxvr858tsaggvf9dw` CHECK (`i` < 5)",
   527  			constraint: ConstraintNamesIgnoreVitess,
   528  		},
   529  		{
   530  			name:       "check constraints, different name, ignore vitess, vitess names match",
   531  			from:       "create table t1 (id int primary key, i int, constraint `check2` CHECK ((`i` < 5)))",
   532  			to:         "create table t2 (id int primary key, i int, constraint `check2_7fp024p4rxvr858tsaggvf9dw` CHECK ((`i` < 5)))",
   533  			diff:       "",
   534  			constraint: ConstraintNamesIgnoreVitess,
   535  		},
   536  		{
   537  			name:       "check constraints, different name, ignore all",
   538  			from:       "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)))",
   539  			to:         "create table t2 (id int primary key, i int, constraint `chk_abc123` CHECK ((`i` < 5)))",
   540  			diff:       "",
   541  			constraint: ConstraintNamesIgnoreAll,
   542  		},
   543  		{
   544  			name: "check constraints, different order",
   545  			from: "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)), constraint `check2` CHECK ((`i` > 2)))",
   546  			to:   "create table t2 (id int primary key, i int, constraint `check2` CHECK ((`i` > 2)), constraint `check1` CHECK ((`i` < 5)))",
   547  			diff: "",
   548  		},
   549  		{
   550  			name:       "check constraints, different names & order",
   551  			from:       "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)), constraint `check2` CHECK ((`i` > 2)))",
   552  			to:         "create table t2 (id int primary key, i int, constraint `chk_123abc` CHECK ((`i` > 2)), constraint `chk_789def` CHECK ((`i` < 5)))",
   553  			diff:       "",
   554  			constraint: ConstraintNamesIgnoreAll,
   555  		},
   556  		{
   557  			name:       "check constraints, add",
   558  			from:       "create table t1 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)), constraint `check2` CHECK ((`i` > 2)))",
   559  			to:         "create table t2 (id int primary key, i int, constraint `chk_123abc` CHECK ((`i` > 2)), constraint `check3` CHECK ((`i` != 3)), constraint `chk_789def` CHECK ((`i` < 5)))",
   560  			diff:       "alter table t1 add constraint check3 check (i != 3)",
   561  			cdiff:      "ALTER TABLE `t1` ADD CONSTRAINT `check3` CHECK (`i` != 3)",
   562  			constraint: ConstraintNamesIgnoreAll,
   563  		},
   564  		{
   565  			name:       "check constraints, remove",
   566  			from:       "create table t1 (id int primary key, i int, constraint `chk_123abc` CHECK ((`i` > 2)), constraint `check3` CHECK ((`i` != 3)), constraint `chk_789def` CHECK ((`i` < 5)))",
   567  			to:         "create table t2 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)), constraint `check2` CHECK ((`i` > 2)))",
   568  			diff:       "alter table t1 drop check check3",
   569  			cdiff:      "ALTER TABLE `t1` DROP CHECK `check3`",
   570  			constraint: ConstraintNamesIgnoreAll,
   571  		},
   572  		{
   573  			name:       "check constraints, remove, ignore vitess, no match",
   574  			from:       "create table t1 (id int primary key, i int, constraint `chk_123abc` CHECK ((`i` > 2)), constraint `check3` CHECK ((`i` != 3)), constraint `chk_789def` CHECK ((`i` < 5)))",
   575  			to:         "create table t2 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)), constraint `check2` CHECK ((`i` > 2)))",
   576  			diff:       "alter table t1 drop check chk_123abc, drop check check3, drop check chk_789def, add constraint check1 check (i < 5), add constraint check2 check (i > 2)",
   577  			cdiff:      "ALTER TABLE `t1` DROP CHECK `chk_123abc`, DROP CHECK `check3`, DROP CHECK `chk_789def`, ADD CONSTRAINT `check1` CHECK (`i` < 5), ADD CONSTRAINT `check2` CHECK (`i` > 2)",
   578  			constraint: ConstraintNamesIgnoreVitess,
   579  		},
   580  		{
   581  			name:       "check constraints, remove, ignore vitess, match",
   582  			from:       "create table t1 (id int primary key, i int, constraint `check2_cukwabxd742sgycn96xj7n87g` CHECK ((`i` > 2)), constraint `check3` CHECK ((`i` != 3)), constraint `check1_19l09s37kbhj4axnzmi10e18k` CHECK ((`i` < 5)))",
   583  			to:         "create table t2 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)), constraint `check2` CHECK ((`i` > 2)))",
   584  			diff:       "alter table t1 drop check check3",
   585  			cdiff:      "ALTER TABLE `t1` DROP CHECK `check3`",
   586  			constraint: ConstraintNamesIgnoreVitess,
   587  		},
   588  		{
   589  			name:       "check constraints, remove, strict",
   590  			from:       "create table t1 (id int primary key, i int, constraint `chk_123abc` CHECK ((`i` > 2)), constraint `check3` CHECK ((`i` != 3)), constraint `chk_789def` CHECK ((`i` < 5)))",
   591  			to:         "create table t2 (id int primary key, i int, constraint `check1` CHECK ((`i` < 5)), constraint `check2` CHECK ((`i` > 2)))",
   592  			diff:       "alter table t1 drop check chk_123abc, drop check check3, drop check chk_789def, add constraint check1 check (i < 5), add constraint check2 check (i > 2)",
   593  			cdiff:      "ALTER TABLE `t1` DROP CHECK `chk_123abc`, DROP CHECK `check3`, DROP CHECK `chk_789def`, ADD CONSTRAINT `check1` CHECK (`i` < 5), ADD CONSTRAINT `check2` CHECK (`i` > 2)",
   594  			constraint: ConstraintNamesStrict,
   595  		},
   596  		// foreign keys
   597  		{
   598  			name:  "drop foreign key",
   599  			from:  "create table t1 (id int primary key, i int, key i_idex (i), constraint f foreign key (i) references parent(id))",
   600  			to:    "create table t2 (id int primary key, i int, key i_idex (i))",
   601  			diff:  "alter table t1 drop foreign key f",
   602  			cdiff: "ALTER TABLE `t1` DROP FOREIGN KEY `f`",
   603  		},
   604  		{
   605  			name:  "add foreign key",
   606  			from:  "create table t1 (id int primary key, i int, key ix(i))",
   607  			to:    "create table t2 (id int primary key, i int, key ix(i), constraint f foreign key (i) references parent(id))",
   608  			diff:  "alter table t1 add constraint f foreign key (i) references parent (id)",
   609  			cdiff: "ALTER TABLE `t1` ADD CONSTRAINT `f` FOREIGN KEY (`i`) REFERENCES `parent` (`id`)",
   610  		},
   611  		{
   612  			name:  "add foreign key and index",
   613  			from:  "create table t1 (id int primary key, i int)",
   614  			to:    "create table t2 (id int primary key, i int, key ix(i), constraint f foreign key (i) references parent(id))",
   615  			diff:  "alter table t1 add key ix (i), add constraint f foreign key (i) references parent (id)",
   616  			cdiff: "ALTER TABLE `t1` ADD KEY `ix` (`i`), ADD CONSTRAINT `f` FOREIGN KEY (`i`) REFERENCES `parent` (`id`)",
   617  		},
   618  		{
   619  			name: "identical foreign key",
   620  			from: "create table t1 (id int primary key, i int, constraint f foreign key (i) references parent(id) on delete cascade)",
   621  			to:   "create table t2 (id int primary key, i int, constraint f foreign key (i) references parent(id) on delete cascade)",
   622  		},
   623  		{
   624  			name: "implicit foreign key indexes",
   625  			from: "create table t1 (id int primary key, i int, key f(i), constraint f foreign key (i) references parent(id) on delete cascade)",
   626  			to:   "create table t2 (id int primary key, i int, constraint f foreign key (i) references parent(id) on delete cascade)",
   627  		},
   628  		{
   629  			name: "implicit foreign key indexes 2",
   630  			from: "create table t1 (id int primary key, i int, constraint f foreign key (i) references parent(id) on delete cascade)",
   631  			to:   "create table t2 (id int primary key, i int, key f(i), constraint f foreign key (i) references parent(id) on delete cascade)",
   632  		},
   633  		{
   634  			name: "implicit unnamed foreign key indexes",
   635  			from: "create table t1 (id int primary key, i int, foreign key (i) references parent(id) on delete cascade)",
   636  			to:   "create table t1 (id int primary key, i int, key i(i), constraint t1_ibfk_1 foreign key (i) references parent(id) on delete cascade)",
   637  		},
   638  		{
   639  			name:  "modify foreign key",
   640  			from:  "create table t1 (id int primary key, i int, key ix(i), constraint f foreign key (i) references parent(id) on delete cascade)",
   641  			to:    "create table t2 (id int primary key, i int, key ix(i), constraint f foreign key (i) references parent(id) on delete set null)",
   642  			diff:  "alter table t1 drop foreign key f, add constraint f foreign key (i) references parent (id) on delete set null",
   643  			cdiff: "ALTER TABLE `t1` DROP FOREIGN KEY `f`, ADD CONSTRAINT `f` FOREIGN KEY (`i`) REFERENCES `parent` (`id`) ON DELETE SET NULL",
   644  		},
   645  		{
   646  			name:  "drop and add foreign key",
   647  			from:  "create table t1 (id int primary key, i int, key ix(i), constraint f foreign key (i) references parent(id) on delete cascade)",
   648  			to:    "create table t2 (id int primary key, i int, key ix(i), constraint f2 foreign key (i) references parent(id) on delete set null)",
   649  			diff:  "alter table t1 drop foreign key f, add constraint f2 foreign key (i) references parent (id) on delete set null",
   650  			cdiff: "ALTER TABLE `t1` DROP FOREIGN KEY `f`, ADD CONSTRAINT `f2` FOREIGN KEY (`i`) REFERENCES `parent` (`id`) ON DELETE SET NULL",
   651  		},
   652  		{
   653  			name: "ignore different foreign key order",
   654  			from: "create table t1 (id int primary key, i int, constraint f foreign key (i) references parent(id) on delete restrict, constraint f2 foreign key (i2) references parent2(id) on delete restrict)",
   655  			to:   "create table t2 (id int primary key, i int, constraint f2 foreign key (i2) references parent2(id) on delete restrict, constraint f foreign key (i) references parent(id) on delete restrict)",
   656  			diff: "",
   657  		},
   658  		{
   659  			name:  "drop foreign key, but not implicit index",
   660  			from:  "create table t1 (id int primary key, i int, constraint f foreign key (i) references parent(id) on delete cascade)",
   661  			to:    "create table t2 (id int primary key, i int, key f(i))",
   662  			diff:  "alter table t1 drop foreign key f",
   663  			cdiff: "ALTER TABLE `t1` DROP FOREIGN KEY `f`",
   664  		},
   665  		// partitions
   666  		{
   667  			name:  "identical partitioning",
   668  			from:  "create table t1 (id int primary key) partition by hash (id) partitions 4",
   669  			to:    "create table t1 (id int primary key, a int) partition by hash (id) partitions 4",
   670  			diff:  "alter table t1 add column a int",
   671  			cdiff: "ALTER TABLE `t1` ADD COLUMN `a` int",
   672  		},
   673  		{
   674  			name:  "partitioning, column case",
   675  			from:  "create table t1 (id int primary key) partition by hash (id) partitions 4",
   676  			to:    "create table t1 (id int primary key, a int) partition by hash (ID) partitions 4",
   677  			diff:  "alter table t1 add column a int \npartition by hash (ID) partitions 4",
   678  			cdiff: "ALTER TABLE `t1` ADD COLUMN `a` int \nPARTITION BY HASH (`ID`) PARTITIONS 4",
   679  		},
   680  		{
   681  			name:  "remove partitioning",
   682  			from:  "create table t1 (id int primary key) partition by hash (id) partitions 4",
   683  			to:    "create table t1 (id int primary key, a int)",
   684  			diff:  "alter table t1 add column a int remove partitioning",
   685  			cdiff: "ALTER TABLE `t1` ADD COLUMN `a` int REMOVE PARTITIONING",
   686  		},
   687  		{
   688  			name:  "remove partitioning 2",
   689  			from:  "create table t1 (id int primary key) partition by hash (id) partitions 4",
   690  			to:    "create table t1 (id int primary key)",
   691  			diff:  "alter table t1 remove partitioning",
   692  			cdiff: "ALTER TABLE `t1` REMOVE PARTITIONING",
   693  		},
   694  		{
   695  			name:  "change partitioning hash",
   696  			from:  "create table t1 (id int primary key) partition by hash (id) partitions 4",
   697  			to:    "create table t1 (id int primary key) partition by hash (id) partitions 5",
   698  			diff:  "alter table t1 \npartition by hash (id) partitions 5",
   699  			cdiff: "ALTER TABLE `t1` \nPARTITION BY HASH (`id`) PARTITIONS 5",
   700  		},
   701  		{
   702  			name:  "change partitioning key",
   703  			from:  "create table t1 (id int primary key) partition by key (id) partitions 2",
   704  			to:    "create table t1 (id int primary key) partition by hash (id) partitions 5",
   705  			diff:  "alter table t1 \npartition by hash (id) partitions 5",
   706  			cdiff: "ALTER TABLE `t1` \nPARTITION BY HASH (`id`) PARTITIONS 5",
   707  		},
   708  		{
   709  			name:  "change partitioning list",
   710  			from:  "create table t1 (id int primary key) partition by key (id) partitions 2",
   711  			to:    "create table t1 (id int primary key) partition by list (id) (partition p1 values in(11,21), partition p2 values in (12,22))",
   712  			diff:  "alter table t1 \npartition by list (id)\n(partition p1 values in (11, 21),\n partition p2 values in (12, 22))",
   713  			cdiff: "ALTER TABLE `t1` \nPARTITION BY LIST (`id`)\n(PARTITION `p1` VALUES IN (11, 21),\n PARTITION `p2` VALUES IN (12, 22))",
   714  		},
   715  		{
   716  			name:  "change partitioning range: rotate",
   717  			from:  "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   718  			to:    "create table t1 (id int primary key) partition by range (id) (partition p2 values less than (20), partition p3 values less than (30), partition p4 values less than (40))",
   719  			diff:  "alter table t1 \npartition by range (id)\n(partition p2 values less than (20),\n partition p3 values less than (30),\n partition p4 values less than (40))",
   720  			cdiff: "ALTER TABLE `t1` \nPARTITION BY RANGE (`id`)\n(PARTITION `p2` VALUES LESS THAN (20),\n PARTITION `p3` VALUES LESS THAN (30),\n PARTITION `p4` VALUES LESS THAN (40))",
   721  		},
   722  		{
   723  			name:     "change partitioning range: ignore rotate",
   724  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   725  			to:       "create table t1 (id int primary key) partition by range (id) (partition p2 values less than (20), partition p3 values less than (30), partition p4 values less than (40))",
   726  			rotation: RangeRotationIgnore,
   727  		},
   728  		{
   729  			name:     "change partitioning range: statements, drop",
   730  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   731  			to:       "create table t1 (id int primary key) partition by range (id) (partition p2 values less than (20), partition p3 values less than (30))",
   732  			rotation: RangeRotationDistinctStatements,
   733  			diff:     "alter table t1 drop partition p1",
   734  			cdiff:    "ALTER TABLE `t1` DROP PARTITION `p1`",
   735  		},
   736  		{
   737  			name:     "change partitioning range: statements, add",
   738  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20))",
   739  			to:       "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   740  			rotation: RangeRotationDistinctStatements,
   741  			diff:     "alter table t1 add partition (partition p3 values less than (30))",
   742  			cdiff:    "ALTER TABLE `t1` ADD PARTITION (PARTITION `p3` VALUES LESS THAN (30))",
   743  		},
   744  		{
   745  			name:     "change partitioning range: statements, multiple drops",
   746  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   747  			to:       "create table t1 (id int primary key) partition by range (id) (partition p3 values less than (30))",
   748  			rotation: RangeRotationDistinctStatements,
   749  			diffs:    []string{"alter table t1 drop partition p1", "alter table t1 drop partition p2"},
   750  			cdiffs:   []string{"ALTER TABLE `t1` DROP PARTITION `p1`", "ALTER TABLE `t1` DROP PARTITION `p2`"},
   751  		},
   752  		{
   753  			name:     "change partitioning range: statements, multiple adds",
   754  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10))",
   755  			to:       "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   756  			rotation: RangeRotationDistinctStatements,
   757  			diffs:    []string{"alter table t1 add partition (partition p2 values less than (20))", "alter table t1 add partition (partition p3 values less than (30))"},
   758  			cdiffs:   []string{"ALTER TABLE `t1` ADD PARTITION (PARTITION `p2` VALUES LESS THAN (20))", "ALTER TABLE `t1` ADD PARTITION (PARTITION `p3` VALUES LESS THAN (30))"},
   759  		},
   760  		{
   761  			name:     "change partitioning range: statements, multiple, assorted",
   762  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   763  			to:       "create table t1 (id int primary key) partition by range (id) (partition p2 values less than (20), partition p3 values less than (30), partition p4 values less than (40))",
   764  			rotation: RangeRotationDistinctStatements,
   765  			diffs:    []string{"alter table t1 drop partition p1", "alter table t1 add partition (partition p4 values less than (40))"},
   766  			cdiffs:   []string{"ALTER TABLE `t1` DROP PARTITION `p1`", "ALTER TABLE `t1` ADD PARTITION (PARTITION `p4` VALUES LESS THAN (40))"},
   767  		},
   768  		{
   769  			name:     "change partitioning range: mixed with nonpartition changes",
   770  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   771  			to:       "create table t1 (id int primary key, i int) partition by range (id) (partition p3 values less than (30))",
   772  			rotation: RangeRotationDistinctStatements,
   773  			diffs:    []string{"alter table t1 add column i int", "alter table t1 drop partition p1", "alter table t1 drop partition p2"},
   774  			cdiffs:   []string{"ALTER TABLE `t1` ADD COLUMN `i` int", "ALTER TABLE `t1` DROP PARTITION `p1`", "ALTER TABLE `t1` DROP PARTITION `p2`"},
   775  		},
   776  		{
   777  			name:     "change partitioning range: single partition change, mixed with nonpartition changes",
   778  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20))",
   779  			to:       "create table t1 (id int primary key, i int) partition by range (id) (partition p2 values less than (20))",
   780  			rotation: RangeRotationDistinctStatements,
   781  			diffs:    []string{"alter table t1 add column i int", "alter table t1 drop partition p1"},
   782  			cdiffs:   []string{"ALTER TABLE `t1` ADD COLUMN `i` int", "ALTER TABLE `t1` DROP PARTITION `p1`"},
   783  		},
   784  		{
   785  			name:     "change partitioning range: mixed with nonpartition changes, full spec",
   786  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   787  			to:       "create table t1 (id int primary key, i int) partition by range (id) (partition p3 values less than (30))",
   788  			rotation: RangeRotationFullSpec,
   789  			diff:     "alter table t1 add column i int \npartition by range (id)\n(partition p3 values less than (30))",
   790  			cdiff:    "ALTER TABLE `t1` ADD COLUMN `i` int \nPARTITION BY RANGE (`id`)\n(PARTITION `p3` VALUES LESS THAN (30))",
   791  		},
   792  		{
   793  			name:     "change partitioning range: ignore rotate, not a rotation",
   794  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   795  			to:       "create table t1 (id int primary key) partition by range (id) (partition p2 values less than (25), partition p3 values less than (30), partition p4 values less than (40))",
   796  			rotation: RangeRotationIgnore,
   797  			diff:     "alter table t1 \npartition by range (id)\n(partition p2 values less than (25),\n partition p3 values less than (30),\n partition p4 values less than (40))",
   798  			cdiff:    "ALTER TABLE `t1` \nPARTITION BY RANGE (`id`)\n(PARTITION `p2` VALUES LESS THAN (25),\n PARTITION `p3` VALUES LESS THAN (30),\n PARTITION `p4` VALUES LESS THAN (40))",
   799  		},
   800  		{
   801  			name:     "change partitioning range: ignore rotate, not a rotation 2",
   802  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   803  			to:       "create table t1 (id int primary key) partition by range (id) (partition p2 values less than (20), partition p3 values less than (35), partition p4 values less than (40))",
   804  			rotation: RangeRotationIgnore,
   805  			diff:     "alter table t1 \npartition by range (id)\n(partition p2 values less than (20),\n partition p3 values less than (35),\n partition p4 values less than (40))",
   806  			cdiff:    "ALTER TABLE `t1` \nPARTITION BY RANGE (`id`)\n(PARTITION `p2` VALUES LESS THAN (20),\n PARTITION `p3` VALUES LESS THAN (35),\n PARTITION `p4` VALUES LESS THAN (40))",
   807  		},
   808  		{
   809  			name:     "change partitioning range: ignore rotate, not a rotation 3",
   810  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   811  			to:       "create table t1 (id int primary key) partition by range (id) (partition p2 values less than (20), partition pX values less than (30), partition p4 values less than (40))",
   812  			rotation: RangeRotationIgnore,
   813  			diff:     "alter table t1 \npartition by range (id)\n(partition p2 values less than (20),\n partition pX values less than (30),\n partition p4 values less than (40))",
   814  			cdiff:    "ALTER TABLE `t1` \nPARTITION BY RANGE (`id`)\n(PARTITION `p2` VALUES LESS THAN (20),\n PARTITION `pX` VALUES LESS THAN (30),\n PARTITION `p4` VALUES LESS THAN (40))",
   815  		},
   816  		{
   817  			name:     "change partitioning range: ignore rotate, not a rotation 4",
   818  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   819  			to:       "create table t1 (id int primary key) partition by range (id) (partition pX values less than (20), partition p3 values less than (30), partition p4 values less than (40))",
   820  			rotation: RangeRotationIgnore,
   821  			diff:     "alter table t1 \npartition by range (id)\n(partition pX values less than (20),\n partition p3 values less than (30),\n partition p4 values less than (40))",
   822  			cdiff:    "ALTER TABLE `t1` \nPARTITION BY RANGE (`id`)\n(PARTITION `pX` VALUES LESS THAN (20),\n PARTITION `p3` VALUES LESS THAN (30),\n PARTITION `p4` VALUES LESS THAN (40))",
   823  		},
   824  		{
   825  			name:     "change partitioning range: ignore rotate, nothing shared",
   826  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   827  			to:       "create table t1 (id int primary key) partition by range (id) (partition p4 values less than (40), partition p5 values less than (50), partition p6 values less than (60))",
   828  			rotation: RangeRotationIgnore,
   829  			diff:     "alter table t1 \npartition by range (id)\n(partition p4 values less than (40),\n partition p5 values less than (50),\n partition p6 values less than (60))",
   830  			cdiff:    "ALTER TABLE `t1` \nPARTITION BY RANGE (`id`)\n(PARTITION `p4` VALUES LESS THAN (40),\n PARTITION `p5` VALUES LESS THAN (50),\n PARTITION `p6` VALUES LESS THAN (60))",
   831  		},
   832  		{
   833  			name:     "change partitioning range: ignore rotate, no names shared, definitions shared",
   834  			from:     "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
   835  			to:       "create table t1 (id int primary key) partition by range (id) (partition pA values less than (20), partition pB values less than (30), partition pC values less than (40))",
   836  			rotation: RangeRotationIgnore,
   837  			diff:     "alter table t1 \npartition by range (id)\n(partition pA values less than (20),\n partition pB values less than (30),\n partition pC values less than (40))",
   838  			cdiff:    "ALTER TABLE `t1` \nPARTITION BY RANGE (`id`)\n(PARTITION `pA` VALUES LESS THAN (20),\n PARTITION `pB` VALUES LESS THAN (30),\n PARTITION `pC` VALUES LESS THAN (40))",
   839  		},
   840  
   841  		//
   842  		// table options
   843  		{
   844  			name: "same options, no diff 1",
   845  			from: "create table t1 (id int primary key) row_format=compressed",
   846  			to:   "create table t1 (id int primary key) row_format=compressed",
   847  		},
   848  		{
   849  			name: "same options, no diff 2",
   850  			from: "create table t1 (id int primary key) row_format=compressed, character set=utf8",
   851  			to:   "create table t1 (id int primary key) row_format=compressed, character set=utf8",
   852  		},
   853  		{
   854  			name: "same options, no diff 3",
   855  			from: "create table t1 (id int primary key) row_format=compressed, character set=utf8",
   856  			to:   "create table t1 (id int primary key) row_format=compressed, charset=utf8",
   857  		},
   858  		{
   859  			name: "reordered options, no diff",
   860  			from: "create table t1 (id int primary key) row_format=compressed character set=utf8",
   861  			to:   "create table t1 (id int primary key) character set=utf8, row_format=compressed",
   862  		},
   863  		{
   864  			name:  "add table option 1",
   865  			from:  "create table t1 (id int primary key)",
   866  			to:    "create table t1 (id int primary key) row_format=compressed",
   867  			diff:  "alter table t1 row_format COMPRESSED",
   868  			cdiff: "ALTER TABLE `t1` ROW_FORMAT COMPRESSED",
   869  		},
   870  		{
   871  			name:  "add table option 2",
   872  			from:  "create table t1 (id int primary key) character set=utf8",
   873  			to:    "create table t1 (id int primary key) character set=utf8, row_format=compressed",
   874  			diff:  "alter table t1 row_format COMPRESSED",
   875  			cdiff: "ALTER TABLE `t1` ROW_FORMAT COMPRESSED",
   876  		},
   877  		{
   878  			name:  "add table option 3",
   879  			from:  "create table t1 (id int primary key) character set=utf8",
   880  			to:    "create table t1 (id int primary key) row_format=compressed, character set=utf8",
   881  			diff:  "alter table t1 row_format COMPRESSED",
   882  			cdiff: "ALTER TABLE `t1` ROW_FORMAT COMPRESSED",
   883  		},
   884  		{
   885  			name:  "add table option 3",
   886  			from:  "create table t1 (id int primary key) character set=utf8",
   887  			to:    "create table t1 (id int primary key) row_format=compressed, character set=utf8, checksum=1",
   888  			diff:  "alter table t1 row_format COMPRESSED checksum 1",
   889  			cdiff: "ALTER TABLE `t1` ROW_FORMAT COMPRESSED CHECKSUM 1",
   890  		},
   891  		{
   892  			name:  "modify table option 1",
   893  			from:  "create table t1 (id int primary key) character set=utf8",
   894  			to:    "create table t1 (id int primary key) character set=utf8mb4",
   895  			diff:  "alter table t1 charset utf8mb4",
   896  			cdiff: "ALTER TABLE `t1` CHARSET utf8mb4",
   897  		},
   898  		{
   899  			name:  "modify table option 2",
   900  			from:  "create table t1 (id int primary key) charset=utf8",
   901  			to:    "create table t1 (id int primary key) character set=utf8mb4",
   902  			diff:  "alter table t1 charset utf8mb4",
   903  			cdiff: "ALTER TABLE `t1` CHARSET utf8mb4",
   904  		},
   905  		{
   906  			name:  "modify table option 3",
   907  			from:  "create table t1 (id int primary key) character set=utf8",
   908  			to:    "create table t1 (id int primary key) charset=utf8mb4",
   909  			diff:  "alter table t1 charset utf8mb4",
   910  			cdiff: "ALTER TABLE `t1` CHARSET utf8mb4",
   911  		},
   912  		{
   913  			name:  "modify table option 4",
   914  			from:  "create table t1 (id int primary key) character set=utf8",
   915  			to:    "create table t1 (id int primary key) row_format=compressed, character set=utf8mb4, checksum=1",
   916  			diff:  "alter table t1 charset utf8mb4 row_format COMPRESSED checksum 1",
   917  			cdiff: "ALTER TABLE `t1` CHARSET utf8mb4 ROW_FORMAT COMPRESSED CHECKSUM 1",
   918  		},
   919  		{
   920  			name:  "remove table option 1",
   921  			from:  "create table t1 (id int primary key) row_format=compressed",
   922  			to:    "create table t1 (id int primary key) ",
   923  			diff:  "alter table t1 row_format DEFAULT",
   924  			cdiff: "ALTER TABLE `t1` ROW_FORMAT DEFAULT",
   925  		},
   926  		{
   927  			name:  "remove table option 2",
   928  			from:  "create table t1 (id int primary key) CHECKSUM=1",
   929  			to:    "create table t1 (id int primary key) ",
   930  			diff:  "alter table t1 checksum 0",
   931  			cdiff: "ALTER TABLE `t1` CHECKSUM 0",
   932  		},
   933  		{
   934  			name:  "remove table option 3",
   935  			from:  "create table t1 (id int primary key) checksum=1",
   936  			to:    "create table t1 (id int primary key) ",
   937  			diff:  "alter table t1 checksum 0",
   938  			cdiff: "ALTER TABLE `t1` CHECKSUM 0",
   939  		},
   940  		{
   941  			name:  "remove table option 4",
   942  			from:  "create table t1 (id int auto_increment primary key) KEY_BLOCK_SIZE=16 COMPRESSION='zlib'",
   943  			to:    "create table t2 (id int auto_increment primary key)",
   944  			diff:  "alter table t1 key_block_size 0 compression ''",
   945  			cdiff: "ALTER TABLE `t1` KEY_BLOCK_SIZE 0 COMPRESSION ''",
   946  		},
   947  		{
   948  			name:  "add, modify and remove table option",
   949  			from:  "create table t1 (id int primary key) engine=innodb, charset=utf8, checksum=1",
   950  			to:    "create table t1 (id int primary key) row_format=compressed, engine=innodb, charset=utf8mb4",
   951  			diff:  "alter table t1 checksum 0 charset utf8mb4 row_format COMPRESSED",
   952  			cdiff: "ALTER TABLE `t1` CHECKSUM 0 CHARSET utf8mb4 ROW_FORMAT COMPRESSED",
   953  		},
   954  		{
   955  			name: "ignore AUTO_INCREMENT addition",
   956  			from: "create table t1 (id int auto_increment primary key)",
   957  			to:   "create table t2 (id int auto_increment primary key) AUTO_INCREMENT=300",
   958  		},
   959  		{
   960  			name:    "apply AUTO_INCREMENT addition",
   961  			from:    "create table t1 (id int auto_increment primary key)",
   962  			to:      "create table t2 (id int auto_increment primary key) AUTO_INCREMENT=300",
   963  			autoinc: AutoIncrementApplyHigher,
   964  			diff:    "alter table t1 auto_increment 300",
   965  			cdiff:   "ALTER TABLE `t1` AUTO_INCREMENT 300",
   966  		},
   967  		{
   968  			name: "ignore AUTO_INCREMENT removal",
   969  			from: "create table t1 (id int auto_increment primary key) AUTO_INCREMENT=300",
   970  			to:   "create table t2 (id int auto_increment primary key)",
   971  		},
   972  		{
   973  			name:    "ignore AUTO_INCREMENT removal 2",
   974  			from:    "create table t1 (id int auto_increment primary key) AUTO_INCREMENT=300",
   975  			to:      "create table t2 (id int auto_increment primary key)",
   976  			autoinc: AutoIncrementApplyHigher,
   977  		},
   978  		{
   979  			name: "ignore AUTO_INCREMENT change",
   980  			from: "create table t1 (id int auto_increment primary key) AUTO_INCREMENT=100",
   981  			to:   "create table t2 (id int auto_increment primary key) AUTO_INCREMENT=300",
   982  		},
   983  		{
   984  			name:    "apply AUTO_INCREMENT change",
   985  			from:    "create table t1 (id int auto_increment primary key) AUTO_INCREMENT=100",
   986  			to:      "create table t2 (id int auto_increment primary key) AUTO_INCREMENT=300",
   987  			autoinc: AutoIncrementApplyHigher,
   988  			diff:    "alter table t1 auto_increment 300",
   989  			cdiff:   "ALTER TABLE `t1` AUTO_INCREMENT 300",
   990  		},
   991  		{
   992  			name:    "ignore AUTO_INCREMENT decrease",
   993  			from:    "create table t1 (id int auto_increment primary key) AUTO_INCREMENT=300",
   994  			to:      "create table t2 (id int auto_increment primary key) AUTO_INCREMENT=100",
   995  			autoinc: AutoIncrementApplyHigher,
   996  		},
   997  		{
   998  			name:    "apply AUTO_INCREMENT decrease",
   999  			from:    "create table t1 (id int auto_increment primary key) AUTO_INCREMENT=300",
  1000  			to:      "create table t2 (id int auto_increment primary key) AUTO_INCREMENT=100",
  1001  			autoinc: AutoIncrementApplyAlways,
  1002  			diff:    "alter table t1 auto_increment 100",
  1003  			cdiff:   "ALTER TABLE `t1` AUTO_INCREMENT 100",
  1004  		},
  1005  		{
  1006  			name:  "apply table charset",
  1007  			from:  "create table t (id int, primary key(id))",
  1008  			to:    "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4",
  1009  			diff:  "alter table t charset utf8mb4",
  1010  			cdiff: "ALTER TABLE `t` CHARSET utf8mb4",
  1011  		},
  1012  		{
  1013  			name:    "ignore empty table charset",
  1014  			from:    "create table t (id int, primary key(id))",
  1015  			to:      "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4",
  1016  			charset: TableCharsetCollateIgnoreEmpty,
  1017  		},
  1018  		{
  1019  			name:    "ignore empty table charset and collate",
  1020  			from:    "create table t (id int, primary key(id))",
  1021  			to:      "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci",
  1022  			charset: TableCharsetCollateIgnoreEmpty,
  1023  		},
  1024  		{
  1025  			name:    "ignore empty table collate",
  1026  			from:    "create table t (id int, primary key(id))",
  1027  			to:      "create table t (id int, primary key(id)) COLLATE utf8mb4_0900_ai_ci",
  1028  			charset: TableCharsetCollateIgnoreEmpty,
  1029  		},
  1030  		{
  1031  			name:    "ignore empty table charset and collate in target",
  1032  			from:    "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci",
  1033  			to:      "create table t (id int, primary key(id))",
  1034  			charset: TableCharsetCollateIgnoreEmpty,
  1035  		},
  1036  		{
  1037  			name:    "ignore dropped collate",
  1038  			from:    "create table t (id int, primary key(id)) COLLATE utf8mb4_0900_ai_ci",
  1039  			to:      "create table t (id int, primary key(id))",
  1040  			charset: TableCharsetCollateIgnoreEmpty,
  1041  		},
  1042  		{
  1043  			name:    "ignore table charset",
  1044  			from:    "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8",
  1045  			to:      "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4",
  1046  			charset: TableCharsetCollateIgnoreAlways,
  1047  		},
  1048  		{
  1049  			name:  "change table charset",
  1050  			from:  "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8",
  1051  			to:    "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4",
  1052  			diff:  "alter table t charset utf8mb4",
  1053  			cdiff: "ALTER TABLE `t` CHARSET utf8mb4",
  1054  		},
  1055  		{
  1056  			name:  `change table charset and columns`,
  1057  			from:  "create table t (id int primary key, t1 varchar(128) default null, t2 varchar(128) not null, t3 tinytext charset latin1, t4 tinytext charset latin1) default charset=utf8",
  1058  			to:    "create table t (id int primary key, t1 varchar(128) not null, t2 varchar(128) not null, t3 tinytext, t4 tinytext charset latin1) default charset=utf8mb4",
  1059  			diff:  "alter table t modify column t1 varchar(128) not null, modify column t2 varchar(128) not null, modify column t3 tinytext, charset utf8mb4",
  1060  			cdiff: "ALTER TABLE `t` MODIFY COLUMN `t1` varchar(128) NOT NULL, MODIFY COLUMN `t2` varchar(128) NOT NULL, MODIFY COLUMN `t3` tinytext, CHARSET utf8mb4",
  1061  		},
  1062  		{
  1063  			name:  "normalized unsigned attribute",
  1064  			from:  "create table t1 (id int primary key)",
  1065  			to:    "create table t1 (id int unsigned primary key)",
  1066  			diff:  "alter table t1 modify column id int unsigned",
  1067  			cdiff: "ALTER TABLE `t1` MODIFY COLUMN `id` int unsigned",
  1068  		},
  1069  		{
  1070  			name:  "normalized ENGINE InnoDB value",
  1071  			from:  "create table t1 (id int primary key) character set=utf8",
  1072  			to:    "create table t1 (id int primary key) engine=innodb, character set=utf8",
  1073  			diff:  "alter table t1 engine InnoDB",
  1074  			cdiff: "ALTER TABLE `t1` ENGINE InnoDB",
  1075  		},
  1076  		{
  1077  			name:  "normalized ENGINE MyISAM value",
  1078  			from:  "create table t1 (id int primary key) character set=utf8",
  1079  			to:    "create table t1 (id int primary key) engine=myisam, character set=utf8",
  1080  			diff:  "alter table t1 engine MyISAM",
  1081  			cdiff: "ALTER TABLE `t1` ENGINE MyISAM",
  1082  		},
  1083  		{
  1084  			name:  "normalized ENGINE MEMORY value",
  1085  			from:  "create table t1 (id int primary key) character set=utf8",
  1086  			to:    "create table t1 (id int primary key) engine=memory, character set=utf8",
  1087  			diff:  "alter table t1 engine MEMORY",
  1088  			cdiff: "ALTER TABLE `t1` ENGINE MEMORY",
  1089  		},
  1090  		{
  1091  			name:  "normalized CHARSET value",
  1092  			from:  "create table t1 (id int primary key) engine=innodb",
  1093  			to:    "create table t1 (id int primary key) engine=innodb, character set=UTF8MB4",
  1094  			diff:  "alter table t1 charset utf8mb4",
  1095  			cdiff: "ALTER TABLE `t1` CHARSET utf8mb4",
  1096  		},
  1097  		{
  1098  			name:  "normalized CHARSET utf8 value",
  1099  			from:  "create table t1 (id int primary key) engine=innodb",
  1100  			to:    "create table t1 (id int primary key) engine=innodb, character set=UTF8",
  1101  			diff:  "alter table t1 charset utf8mb3",
  1102  			cdiff: "ALTER TABLE `t1` CHARSET utf8mb3",
  1103  		},
  1104  		{
  1105  			name:  "normalized COLLATE value",
  1106  			from:  "create table t1 (id int primary key) engine=innodb",
  1107  			to:    "create table t1 (id int primary key) engine=innodb, collate=UTF8_BIN",
  1108  			diff:  "alter table t1 collate utf8mb3_bin",
  1109  			cdiff: "ALTER TABLE `t1` COLLATE utf8mb3_bin",
  1110  		},
  1111  		{
  1112  			name:  "remove table comment",
  1113  			from:  "create table t1 (id int primary key) comment='foo'",
  1114  			to:    "create table t1 (id int primary key)",
  1115  			diff:  "alter table t1 comment ''",
  1116  			cdiff: "ALTER TABLE `t1` COMMENT ''",
  1117  		},
  1118  		// algorithm
  1119  		{
  1120  			name:      "algorithm: COPY",
  1121  			from:      "create table t1 (`id` int primary key)",
  1122  			to:        "create table t2 (id int primary key, `i` int not null default 0)",
  1123  			diff:      "alter table t1 add column i int not null default 0, algorithm = COPY",
  1124  			cdiff:     "ALTER TABLE `t1` ADD COLUMN `i` int NOT NULL DEFAULT 0, ALGORITHM = COPY",
  1125  			algorithm: AlterTableAlgorithmStrategyCopy,
  1126  		},
  1127  		{
  1128  			name:      "algorithm: INPLACE",
  1129  			from:      "create table t1 (`id` int primary key)",
  1130  			to:        "create table t2 (id int primary key, `i` int not null default 0)",
  1131  			diff:      "alter table t1 add column i int not null default 0, algorithm = INPLACE",
  1132  			cdiff:     "ALTER TABLE `t1` ADD COLUMN `i` int NOT NULL DEFAULT 0, ALGORITHM = INPLACE",
  1133  			algorithm: AlterTableAlgorithmStrategyInplace,
  1134  		},
  1135  		{
  1136  			name:      "algorithm: INSTANT",
  1137  			from:      "create table t1 (`id` int primary key)",
  1138  			to:        "create table t2 (id int primary key, `i` int not null default 0)",
  1139  			diff:      "alter table t1 add column i int not null default 0, algorithm = INSTANT",
  1140  			cdiff:     "ALTER TABLE `t1` ADD COLUMN `i` int NOT NULL DEFAULT 0, ALGORITHM = INSTANT",
  1141  			algorithm: AlterTableAlgorithmStrategyInstant,
  1142  		},
  1143  	}
  1144  	standardHints := DiffHints{}
  1145  	for _, ts := range tt {
  1146  		t.Run(ts.name, func(t *testing.T) {
  1147  			fromStmt, err := sqlparser.ParseStrictDDL(ts.from)
  1148  			require.NoError(t, err)
  1149  			fromCreateTable, ok := fromStmt.(*sqlparser.CreateTable)
  1150  			require.True(t, ok)
  1151  
  1152  			toStmt, err := sqlparser.ParseStrictDDL(ts.to)
  1153  			require.NoError(t, err)
  1154  			toCreateTable, ok := toStmt.(*sqlparser.CreateTable)
  1155  			require.True(t, ok)
  1156  
  1157  			c, err := NewCreateTableEntity(fromCreateTable)
  1158  			require.NoError(t, err)
  1159  			other, err := NewCreateTableEntity(toCreateTable)
  1160  			require.NoError(t, err)
  1161  
  1162  			hints := standardHints
  1163  			hints.AutoIncrementStrategy = ts.autoinc
  1164  			hints.RangeRotationStrategy = ts.rotation
  1165  			hints.ConstraintNamesStrategy = ts.constraint
  1166  			hints.ColumnRenameStrategy = ts.colrename
  1167  			hints.FullTextKeyStrategy = ts.fulltext
  1168  			hints.TableCharsetCollateStrategy = ts.charset
  1169  			hints.AlterTableAlgorithmStrategy = ts.algorithm
  1170  			alter, err := c.Diff(other, &hints)
  1171  
  1172  			require.Equal(t, len(ts.diffs), len(ts.cdiffs))
  1173  			if ts.diff == "" && len(ts.diffs) > 0 {
  1174  				ts.diff = ts.diffs[0]
  1175  				ts.cdiff = ts.cdiffs[0]
  1176  			}
  1177  			switch {
  1178  			case ts.isError:
  1179  				require.Error(t, err)
  1180  				if ts.errorMsg != "" {
  1181  					assert.Contains(t, err.Error(), ts.errorMsg)
  1182  				}
  1183  			case ts.diff == "":
  1184  				assert.NoError(t, err)
  1185  				assert.True(t, alter.IsEmpty(), "expected empty diff, found changes")
  1186  				if !alter.IsEmpty() {
  1187  					t.Logf("statements[0]: %v", alter.StatementString())
  1188  					t.Logf("c: %v", sqlparser.CanonicalString(c.CreateTable))
  1189  					t.Logf("other: %v", sqlparser.CanonicalString(other.CreateTable))
  1190  				}
  1191  			default:
  1192  				assert.NoError(t, err)
  1193  				require.NotNil(t, alter)
  1194  				assert.False(t, alter.IsEmpty(), "expected changes, found empty diff")
  1195  
  1196  				{
  1197  					diff := alter.StatementString()
  1198  					assert.Equal(t, ts.diff, diff)
  1199  
  1200  					if len(ts.diffs) > 0 {
  1201  
  1202  						allSubsequentDiffs := AllSubsequent(alter)
  1203  						require.Equal(t, len(ts.diffs), len(allSubsequentDiffs))
  1204  						require.Equal(t, len(ts.cdiffs), len(allSubsequentDiffs))
  1205  						for i := range ts.diffs {
  1206  							assert.Equal(t, ts.diffs[i], allSubsequentDiffs[i].StatementString())
  1207  							assert.Equal(t, ts.cdiffs[i], allSubsequentDiffs[i].CanonicalStatementString())
  1208  						}
  1209  					}
  1210  					// validate we can parse back the statement
  1211  					_, err := sqlparser.ParseStrictDDL(diff)
  1212  					assert.NoError(t, err)
  1213  
  1214  					// Validate "from/to" entities
  1215  					eFrom, eTo := alter.Entities()
  1216  					if ts.fromName != "" {
  1217  						assert.Equal(t, ts.fromName, eFrom.Name())
  1218  					}
  1219  					if ts.toName != "" {
  1220  						assert.Equal(t, ts.toName, eTo.Name())
  1221  					}
  1222  
  1223  					{ // Validate "apply()" on "from" converges with "to"
  1224  						applied, err := c.Apply(alter)
  1225  						assert.NoError(t, err)
  1226  						require.NotNil(t, applied)
  1227  						appliedDiff, err := eTo.Diff(applied, &hints)
  1228  						require.NoError(t, err)
  1229  						assert.True(t, appliedDiff.IsEmpty(), "expected empty diff, found changes: %v.\nc=%v\n,alter=%v\n,eTo=%v\napplied=%v\n",
  1230  							appliedDiff.CanonicalStatementString(),
  1231  							c.Create().CanonicalStatementString(),
  1232  							alter.CanonicalStatementString(),
  1233  							eTo.Create().CanonicalStatementString(),
  1234  							applied.Create().CanonicalStatementString(),
  1235  						)
  1236  					}
  1237  				}
  1238  				{
  1239  					cdiff := alter.CanonicalStatementString()
  1240  					assert.Equal(t, ts.cdiff, cdiff)
  1241  					_, err := sqlparser.ParseStrictDDL(cdiff)
  1242  					assert.NoError(t, err)
  1243  				}
  1244  
  1245  			}
  1246  		})
  1247  	}
  1248  }
  1249  
  1250  func TestValidate(t *testing.T) {
  1251  	tt := []struct {
  1252  		name      string
  1253  		from      string
  1254  		to        string
  1255  		alter     string
  1256  		expectErr error
  1257  	}{
  1258  		// columns
  1259  		{
  1260  			name:  "add column",
  1261  			from:  "create table t (id int primary key)",
  1262  			alter: "alter table t add column i int",
  1263  			to:    "create table t (id int primary key, i int)",
  1264  		},
  1265  		{
  1266  			name:      "duplicate existing column",
  1267  			from:      "create table t (id int primary key, id varchar(10))",
  1268  			alter:     "alter table t add column i int",
  1269  			expectErr: &ApplyDuplicateColumnError{Table: "t", Column: "id"},
  1270  		},
  1271  		// keys
  1272  		{
  1273  			name:  "add key",
  1274  			from:  "create table t (id int primary key, i int)",
  1275  			alter: "alter table t add key i_idx(i)",
  1276  			to:    "create table t (id int primary key, i int, key i_idx(i))",
  1277  		},
  1278  		{
  1279  			name:      "invalid table definition: primary key, same columns",
  1280  			from:      "create table t (id int primary key, i int, primary key (id))",
  1281  			alter:     "alter table t engine=innodb",
  1282  			expectErr: &DuplicateKeyNameError{Table: "t", Key: "PRIMARY"},
  1283  		},
  1284  		{
  1285  			name:      "invalid table definition: primary key, different column",
  1286  			from:      "create table t (id int primary key, i int, primary key (i))",
  1287  			alter:     "alter table t engine=innodb",
  1288  			expectErr: &DuplicateKeyNameError{Table: "t", Key: "PRIMARY"},
  1289  		},
  1290  		{
  1291  			name:  "add primary key",
  1292  			from:  "create table t (id int, i int)",
  1293  			alter: "alter table t add primary key(id)",
  1294  			to:    "create table t (id int, i int, primary key (id))",
  1295  		},
  1296  		{
  1297  			name:  "add primary key with existing key",
  1298  			from:  "create table t (id int, i int, key i_idx (i))",
  1299  			alter: "alter table t add primary key(id)",
  1300  			to:    "create table t (id int, i int, primary key (id), key i_idx (i))",
  1301  		},
  1302  		{
  1303  			name:  "modify into primary key",
  1304  			from:  "create table t (id int, i int)",
  1305  			alter: "alter table t modify id int primary key",
  1306  			to:    "create table t (id int, i int, primary key (id))",
  1307  		},
  1308  		{
  1309  			name:  "modify a primary key column",
  1310  			from:  "create table t (id int primary key, i int)",
  1311  			alter: "alter table t modify id bigint primary key",
  1312  			to:    "create table t (id bigint, i int, primary key (id))",
  1313  		},
  1314  		{
  1315  			name:  "modify a primary key column 2",
  1316  			from:  "create table t (id int, i int, primary key (id))",
  1317  			alter: "alter table t modify id bigint primary key",
  1318  			to:    "create table t (id bigint, i int, primary key (id))",
  1319  		},
  1320  		{
  1321  			name:      "fail modify another column to primary key",
  1322  			from:      "create table t (id int primary key, i int)",
  1323  			alter:     "alter table t modify i int primary key",
  1324  			expectErr: &DuplicateKeyNameError{Table: "t", Key: "PRIMARY"},
  1325  		},
  1326  		{
  1327  			name:      "fail add another primary key column",
  1328  			from:      "create table t (id int primary key, i int)",
  1329  			alter:     "alter table t add column i2 int primary key",
  1330  			expectErr: &DuplicateKeyNameError{Table: "t", Key: "PRIMARY"},
  1331  		},
  1332  		{
  1333  			name:      "fail add another primary key",
  1334  			from:      "create table t (id int primary key, i int)",
  1335  			alter:     "alter table t add primary key (i)",
  1336  			expectErr: &DuplicateKeyNameError{Table: "t", Key: "PRIMARY"},
  1337  		},
  1338  		{
  1339  			name:  "add key, column case",
  1340  			from:  "create table t (id int primary key, i int)",
  1341  			alter: "alter table t add key i_idx(I)",
  1342  			to:    "create table t (id int primary key, i int, key i_idx(I))",
  1343  		},
  1344  		{
  1345  			name:  "add column and key",
  1346  			from:  "create table t (id int primary key)",
  1347  			alter: "alter table t add column i int, add key i_idx(i)",
  1348  			to:    "create table t (id int primary key, i int, key i_idx(i))",
  1349  		},
  1350  		{
  1351  			name:      "add key, missing column",
  1352  			from:      "create table t (id int primary key, i int)",
  1353  			alter:     "alter table t add key j_idx(j)",
  1354  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "j", Key: "j_idx"},
  1355  		},
  1356  		{
  1357  			name:      "add key, missing column 2",
  1358  			from:      "create table t (id int primary key, i int)",
  1359  			alter:     "alter table t add key j_idx(j, i)",
  1360  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "j", Key: "j_idx"},
  1361  		},
  1362  		{
  1363  			name:  "drop column, ok",
  1364  			from:  "create table t (id int primary key, i int, i2 int, key i_idx(i))",
  1365  			alter: "alter table t drop column i2",
  1366  			to:    "create table t (id int primary key, i int, key i_idx(i))",
  1367  		},
  1368  		{
  1369  			name:  "drop and add same column, ok",
  1370  			from:  "create table t (id int primary key, i int, i2 int, key i_idx(i))",
  1371  			alter: "alter table t drop column i2, add column i2 bigint not null",
  1372  			to:    "create table t (id int primary key, i int, i2 bigint not null, key i_idx(i))",
  1373  		},
  1374  		{
  1375  			name:  "drop column, affect keys",
  1376  			from:  "create table t (id int primary key, i int, key i_idx(i))",
  1377  			alter: "alter table t drop column i",
  1378  			to:    "create table t (id int primary key)",
  1379  		},
  1380  		{
  1381  			name:  "drop column, affect keys 2",
  1382  			from:  "create table t (id int primary key, i int, i2 int, key i_idx(i, i2))",
  1383  			alter: "alter table t drop column i",
  1384  			to:    "create table t (id int primary key, i2 int, key i_idx(i2))",
  1385  		},
  1386  		{
  1387  			name:  "drop column, affect keys 3",
  1388  			from:  "create table t (id int primary key, i int, i2 int, key i_idx(i, i2))",
  1389  			alter: "alter table t drop column i2",
  1390  			to:    "create table t (id int primary key, i int, key i_idx(i))",
  1391  		},
  1392  		{
  1393  			name:  "drop column, affect keys 4",
  1394  			from:  "create table t (id int primary key, i int, i2 int, key some_key(id, i), key i_idx(i, i2))",
  1395  			alter: "alter table t drop column i2",
  1396  			to:    "create table t (id int primary key, i int, key some_key(id, i), key i_idx(i))",
  1397  		},
  1398  		{
  1399  			name:  "drop column, affect keys 4, column case",
  1400  			from:  "create table t (id int primary key, i int, i2 int, key some_key(id, i), key i_idx(i, I2))",
  1401  			alter: "alter table t drop column i2",
  1402  			to:    "create table t (id int primary key, i int, key some_key(id, i), key i_idx(i))",
  1403  		},
  1404  		{
  1405  			name:      "drop column, affect keys with expression",
  1406  			from:      "create table t (id int primary key, i int, key id_idx((IF(id, 0, 1))), key i_idx((IF(i,0,1))))",
  1407  			alter:     "alter table t drop column i",
  1408  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "i", Key: "i_idx"},
  1409  		},
  1410  		{
  1411  			name:      "drop column, affect keys with expression and multi expressions",
  1412  			from:      "create table t (id int primary key, i int, key id_idx((IF(id, 0, 1))), key i_idx((IF(i,0,1)), (IF(id,2,3))))",
  1413  			alter:     "alter table t drop column i",
  1414  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "i", Key: "i_idx"},
  1415  		},
  1416  		{
  1417  			name:  "add multiple keys, multi columns, ok",
  1418  			from:  "create table t (id int primary key, i1 int, i2 int, i3 int)",
  1419  			alter: "alter table t add key i12_idx(i1, i2), add key i32_idx(i3, i2), add key i21_idx(i2, i1)",
  1420  			to:    "create table t (id int primary key, i1 int, i2 int, i3 int, key i12_idx(i1, i2), key i32_idx(i3, i2), key i21_idx(i2, i1))",
  1421  		},
  1422  		{
  1423  			name:      "add multiple keys, multi columns, missing column",
  1424  			from:      "create table t (id int primary key, i1 int, i2 int, i4 int)",
  1425  			alter:     "alter table t add key i12_idx(i1, i2), add key i32_idx(i3, i2), add key i21_idx(i2, i1)",
  1426  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "i3", Key: "i32_idx"},
  1427  		},
  1428  		{
  1429  			name:      "multiple primary keys",
  1430  			from:      "create table t (id int primary key, i1 int, i2 int, primary key (i1))",
  1431  			alter:     "alter table t engine=innodb",
  1432  			expectErr: &DuplicateKeyNameError{Table: "t", Key: "PRIMARY"},
  1433  		},
  1434  		{
  1435  			name:      "multiple primary keys for same column",
  1436  			from:      "create table t (id int primary key, i1 int, i2 int, primary key (id))",
  1437  			alter:     "alter table t engine=innodb",
  1438  			expectErr: &DuplicateKeyNameError{Table: "t", Key: "PRIMARY"},
  1439  		},
  1440  		// partitions
  1441  		{
  1442  			name:      "drop column used by partitions",
  1443  			from:      "create table t (id int, i int, primary key (id, i), unique key i_idx(i)) partition by hash (i) partitions 4",
  1444  			alter:     "alter table t drop column i",
  1445  			expectErr: &InvalidColumnInPartitionError{Table: "t", Column: "i"},
  1446  		},
  1447  		{
  1448  			name:      "drop column used by partitions, column case",
  1449  			from:      "create table t (id int, i int, primary key (id, i), unique key i_idx(i)) partition by hash (I) partitions 4",
  1450  			alter:     "alter table t drop column i",
  1451  			expectErr: &InvalidColumnInPartitionError{Table: "t", Column: "I"},
  1452  		},
  1453  		{
  1454  			name:      "drop column used by partitions, function",
  1455  			from:      "create table t (id int, i int, primary key (id, i), unique key i_idx(i)) partition by hash (abs(i)) partitions 4",
  1456  			alter:     "alter table t drop column i",
  1457  			expectErr: &InvalidColumnInPartitionError{Table: "t", Column: "i"},
  1458  		},
  1459  		{
  1460  			name:  "unique key covers all partitioned columns",
  1461  			from:  "create table t (id int, i int, primary key (id, i)) partition by hash (i) partitions 4",
  1462  			alter: "alter table t add unique key i_idx(i)",
  1463  			to:    "create table t (id int, i int, primary key (id, i), unique key i_idx(i)) partition by hash (i) partitions 4",
  1464  		},
  1465  		{
  1466  			name:      "unique key does not cover all partitioned columns",
  1467  			from:      "create table t (id int, i int, primary key (id, i)) partition by hash (i) partitions 4",
  1468  			alter:     "alter table t add unique key id_idx(id)",
  1469  			expectErr: &MissingPartitionColumnInUniqueKeyError{Table: "t", Column: "i", UniqueKey: "id_idx"},
  1470  		},
  1471  		{
  1472  			name:      "add multiple keys, multi columns, missing column",
  1473  			from:      "create table t (id int primary key, i1 int, i2 int, i4 int)",
  1474  			alter:     "alter table t add key i12_idx(i1, i2), add key i32_idx((IF(i3 IS NULL, i2, i3)), i2), add key i21_idx(i2, i1)",
  1475  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "i3", Key: "i32_idx"},
  1476  		},
  1477  		// data types
  1478  		{
  1479  			name:  "nullable timestamp",
  1480  			from:  "create table t (id int primary key, t datetime)",
  1481  			alter: "alter table t modify column t timestamp null",
  1482  			to:    "create table t (id int primary key, t timestamp null)",
  1483  		},
  1484  		{
  1485  			name:  "add range partition",
  1486  			from:  "create table t (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20))",
  1487  			alter: "alter table t add partition (partition p3 values less than (30))",
  1488  			to:    "create table t (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30))",
  1489  		},
  1490  		{
  1491  			name:      "add range partition, duplicate",
  1492  			from:      "create table t (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20))",
  1493  			alter:     "alter table t add partition (partition p2 values less than (30))",
  1494  			expectErr: &ApplyDuplicatePartitionError{Table: "t", Partition: "p2"},
  1495  		},
  1496  		{
  1497  			name:      "add range partition, duplicate",
  1498  			from:      "create table t (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20))",
  1499  			alter:     "alter table t add partition (partition P2 values less than (30))",
  1500  			expectErr: &ApplyDuplicatePartitionError{Table: "t", Partition: "P2"},
  1501  		},
  1502  		{
  1503  			name:      "add range partition, no partitioning",
  1504  			from:      "create table t (id int primary key)",
  1505  			alter:     "alter table t add partition (partition p2 values less than (30))",
  1506  			expectErr: &ApplyNoPartitionsError{Table: "t"},
  1507  		},
  1508  		{
  1509  			name:  "drop range partition",
  1510  			from:  "create table t (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20))",
  1511  			alter: "alter table t drop partition p1",
  1512  			to:    "create table t (id int primary key) partition by range (id) (partition p2 values less than (20))",
  1513  		},
  1514  		{
  1515  			name:      "drop range partition, not found",
  1516  			from:      "create table t (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20))",
  1517  			alter:     "alter table t drop partition p7",
  1518  			expectErr: &ApplyPartitionNotFoundError{Table: "t", Partition: "p7"},
  1519  		},
  1520  		{
  1521  			name:      "duplicate existing partition name",
  1522  			from:      "create table t1 (id int primary key) partition by range (id) (partition p1 values less than (10), partition p2 values less than (20), partition p2 values less than (30))",
  1523  			alter:     "alter table t add column i int",
  1524  			expectErr: &ApplyDuplicatePartitionError{Table: "t1", Partition: "p2"},
  1525  		},
  1526  		{
  1527  			name:  "change to visible with alter column",
  1528  			from:  "create table t (id int, i int invisible, primary key (id))",
  1529  			alter: "alter table t alter column i set visible",
  1530  			to:    "create table t (id int, i int, primary key (id))",
  1531  		},
  1532  		{
  1533  			name:  "change to invisible with alter column",
  1534  			from:  "create table t (id int, i int, primary key (id))",
  1535  			alter: "alter table t alter column i set invisible",
  1536  			to:    "create table t (id int, i int invisible, primary key (id))",
  1537  		},
  1538  		{
  1539  			name:  "remove default with alter column",
  1540  			from:  "create table t (id int, i int default 0, primary key (id))",
  1541  			alter: "alter table t alter column i drop default",
  1542  			to:    "create table t (id int, i int, primary key (id))",
  1543  		},
  1544  		{
  1545  			name:  "change default with alter column",
  1546  			from:  "create table t (id int, i int, primary key (id))",
  1547  			alter: "alter table t alter column i set default 0",
  1548  			to:    "create table t (id int, i int default 0, primary key (id))",
  1549  		},
  1550  		{
  1551  			name:  "change to visible with alter index",
  1552  			from:  "create table t (id int primary key, i int, key i_idx(i) invisible)",
  1553  			alter: "alter table t alter index i_idx visible",
  1554  			to:    "create table t (id int primary key, i int, key i_idx(i))",
  1555  		},
  1556  		{
  1557  			name:  "change to invisible with alter index",
  1558  			from:  "create table t (id int primary key, i int, key i_idx(i))",
  1559  			alter: "alter table t alter index i_idx invisible",
  1560  			to:    "create table t (id int primary key, i int, key i_idx(i) invisible)",
  1561  		},
  1562  		{
  1563  			name:      "drop column used by a generated column",
  1564  			from:      "create table t (id int, i int, neg int as (0-i), primary key (id))",
  1565  			alter:     "alter table t drop column i",
  1566  			expectErr: &InvalidColumnInGeneratedColumnError{Table: "t", Column: "i", GeneratedColumn: "neg"},
  1567  		},
  1568  		{
  1569  			name:      "drop column used by a generated column, column case",
  1570  			from:      "create table t (id int, i int, neg int as (0-I), primary key (id))",
  1571  			alter:     "alter table t drop column I",
  1572  			expectErr: &InvalidColumnInGeneratedColumnError{Table: "t", Column: "I", GeneratedColumn: "neg"},
  1573  		},
  1574  		{
  1575  			name:      "add generated column referencing nonexistent column",
  1576  			from:      "create table t (id int, primary key (id))",
  1577  			alter:     "alter table t add column neg int as (0-i)",
  1578  			expectErr: &InvalidColumnInGeneratedColumnError{Table: "t", Column: "i", GeneratedColumn: "neg"},
  1579  		},
  1580  		{
  1581  			name:  "add generated column referencing existing column",
  1582  			from:  "create table t (id int, i int not null default 0, primary key (id))",
  1583  			alter: "alter table t add column neg int as (0-i)",
  1584  			to:    "create table t (id int, i int not null default 0, neg int as (0-i), primary key (id))",
  1585  		},
  1586  		{
  1587  			name:      "drop column used by a functional index",
  1588  			from:      "create table t (id int, d datetime, primary key (id), key m ((month(d))))",
  1589  			alter:     "alter table t drop column d",
  1590  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "d", Key: "m"},
  1591  		},
  1592  		{
  1593  			name:      "add generated column referencing nonexistent column",
  1594  			from:      "create table t (id int, primary key (id))",
  1595  			alter:     "alter table t add index m ((month(d)))",
  1596  			expectErr: &InvalidColumnInKeyError{Table: "t", Column: "d", Key: "m"},
  1597  		},
  1598  		{
  1599  			name:  "add functional index referencing existing column",
  1600  			from:  "create table t (id int, d datetime, primary key (id))",
  1601  			alter: "alter table t add index m ((month(d)))",
  1602  			to:    "create table t (id int, d datetime, primary key (id), key m ((month(d))))",
  1603  		},
  1604  		// This case slightly diverges right now from MySQL behavior where a referenced column
  1605  		// gets normalized to the casing it has in the table definition. This version here
  1606  		// still works though.
  1607  		{
  1608  			name:  "add functional index referencing existing column with different case",
  1609  			from:  "create table t (id int, d datetime, primary key (id))",
  1610  			alter: "alter table t add index m ((month(D)))",
  1611  			to:    "create table t (id int, d datetime, primary key (id), key m ((month(D))))",
  1612  		},
  1613  		{
  1614  			name:  "constraint check which only uses single drop column",
  1615  			from:  "create table t (id int, d datetime, primary key (id), constraint unix_epoch check (d < '1970-01-01'))",
  1616  			alter: "alter table t drop column d",
  1617  			to:    "create table t (id int, primary key (id))",
  1618  		},
  1619  		{
  1620  			name:      "constraint check which uses multiple dropped columns",
  1621  			from:      "create table t (id int, d datetime, e datetime, primary key (id), constraint unix_epoch check (d < '1970-01-01' and e < '1970-01-01'))",
  1622  			alter:     "alter table t drop column d, drop column e",
  1623  			expectErr: &InvalidColumnInCheckConstraintError{Table: "t", Constraint: "unix_epoch", Column: "d"},
  1624  		},
  1625  		{
  1626  			name:      "constraint check which uses multiple dropped columns",
  1627  			from:      "create table t (id int, d datetime, e datetime, primary key (id), constraint unix_epoch check (d < '1970-01-01' and e < '1970-01-01'))",
  1628  			alter:     "alter table t drop column e",
  1629  			expectErr: &InvalidColumnInCheckConstraintError{Table: "t", Constraint: "unix_epoch", Column: "e"},
  1630  		},
  1631  		{
  1632  			name:  "constraint check added",
  1633  			from:  "create table t (id int, d datetime, e datetime, primary key (id))",
  1634  			alter: "alter table t add constraint unix_epoch check (d < '1970-01-01' and e < '1970-01-01')",
  1635  			to:    "create table t (id int, d datetime, e datetime, primary key (id), constraint unix_epoch check (d < '1970-01-01' and e < '1970-01-01'))",
  1636  		},
  1637  		{
  1638  			name:      "constraint check added with invalid column",
  1639  			from:      "create table t (id int, d datetime, e datetime, primary key (id))",
  1640  			alter:     "alter table t add constraint unix_epoch check (d < '1970-01-01' and f < '1970-01-01')",
  1641  			expectErr: &InvalidColumnInCheckConstraintError{Table: "t", Constraint: "unix_epoch", Column: "f"},
  1642  		},
  1643  		{
  1644  			name:  "constraint check added with camelcase column",
  1645  			from:  "create table t (id int, dateT datetime, e datetime, primary key (id))",
  1646  			alter: "alter table t add constraint unix_epoch check (dateT < '1970-01-01')",
  1647  			to:    "create table t (id int, dateT datetime, e datetime, primary key (id), constraint unix_epoch check (dateT < '1970-01-01'))",
  1648  		},
  1649  		{
  1650  			name:  "constraint check added with camelcase column",
  1651  			from:  "create table t (id int, dateT datetime, e datetime, primary key (id), constraint unix_epoch check (dateT < '1970-01-01'))",
  1652  			alter: "alter table t drop column e",
  1653  			to:    "create table t (id int, dateT datetime, primary key (id), constraint unix_epoch check (dateT < '1970-01-01'))",
  1654  		},
  1655  		// Foreign keys
  1656  		{
  1657  			name:      "existing foreign key, no such column",
  1658  			from:      "create table t (id int primary key, i int, constraint f foreign key (z) references parent(id))",
  1659  			alter:     "alter table t engine=innodb",
  1660  			expectErr: &InvalidColumnInForeignKeyConstraintError{Table: "t", Constraint: "f", Column: "z"},
  1661  		},
  1662  		{
  1663  			name:      "add foreign key, no such column",
  1664  			from:      "create table t (id int primary key, i int)",
  1665  			alter:     "alter table t add constraint f foreign key (z) references parent(id)",
  1666  			expectErr: &InvalidColumnInForeignKeyConstraintError{Table: "t", Constraint: "f", Column: "z"},
  1667  		},
  1668  		{
  1669  			name:      "mismatching column count in foreign key",
  1670  			from:      "create table t (id int primary key, i int, constraint f foreign key (i) references parent(id, z))",
  1671  			alter:     "alter table t engine=innodb",
  1672  			expectErr: &ForeignKeyColumnCountMismatchError{Table: "t", Constraint: "f", ColumnCount: 1, ReferencedTable: "parent", ReferencedColumnCount: 2},
  1673  		},
  1674  		{
  1675  			name:  "change with constraints with uppercase columns",
  1676  			from:  "CREATE TABLE `Machine` (id int primary key, `a` int, `B` int, CONSTRAINT `chk` CHECK (`B` >= `a`))",
  1677  			alter: "ALTER TABLE `Machine` MODIFY COLUMN `id` bigint primary key",
  1678  			to:    "CREATE TABLE `Machine` (id bigint primary key, `a` int, `B` int, CONSTRAINT `chk` CHECK (`B` >= `a`))",
  1679  		},
  1680  		{
  1681  			name:  "add unnamed foreign key, implicitly add index",
  1682  			from:  "create table t (id int primary key, i int)",
  1683  			alter: "alter table t add foreign key (i) references parent(id)",
  1684  			to:    "create table t (id int primary key, i int, key i (i), constraint t_ibfk_1 foreign key (i) references parent(id))",
  1685  		},
  1686  		{
  1687  			name:  "add foreign key, implicitly add index",
  1688  			from:  "create table t (id int primary key, i int)",
  1689  			alter: "alter table t add constraint f foreign key (i) references parent(id)",
  1690  			to:    "create table t (id int primary key, i int, key f (i), constraint f foreign key (i) references parent(id))",
  1691  		},
  1692  		{
  1693  			name:  "add foreign key and index, no implicit index",
  1694  			from:  "create table t (id int primary key, i int)",
  1695  			alter: "alter table t add key i_idx (i), add constraint f foreign key (i) references parent(id)",
  1696  			to:    "create table t (id int primary key, i int, key i_idx (i), constraint f foreign key (i) references parent(id))",
  1697  		},
  1698  		{
  1699  			name:  "add foreign key and extended index, no implicit index",
  1700  			from:  "create table t (id int primary key, i int)",
  1701  			alter: "alter table t add key i_id_idx (i, id), add constraint f foreign key (i) references parent(id)",
  1702  			to:    "create table t (id int primary key, i int, key i_id_idx (i, id), constraint f foreign key (i) references parent(id))",
  1703  		},
  1704  		{
  1705  			name:      "add foreign key, implicitly add index, fail duplicate key name",
  1706  			from:      "create table t (id int primary key, i int, key f(id, i))",
  1707  			alter:     "alter table t add constraint f foreign key (i) references parent(id)",
  1708  			expectErr: &ApplyDuplicateKeyError{Table: "t", Key: "f"},
  1709  		},
  1710  		{
  1711  			name:      "fail drop key leaving unindexed foreign key constraint",
  1712  			from:      "create table t (id int primary key, i int, key i (i), constraint f foreign key (i) references parent(id))",
  1713  			alter:     "alter table t drop key `i`",
  1714  			expectErr: &IndexNeededByForeignKeyError{Table: "t", Key: "i"},
  1715  		},
  1716  		{
  1717  			name:  "drop key with alternative key for foreign key constraint, 1",
  1718  			from:  "create table t (id int primary key, i int, key i (i), key i2 (i, id), constraint f foreign key (i) references parent(id))",
  1719  			alter: "alter table t drop key `i`",
  1720  			to:    "create table t (id int primary key, i int, key i2 (i, id), constraint f foreign key (i) references parent(id))",
  1721  		},
  1722  		{
  1723  			name:  "drop key with alternative key for foreign key constraint, 2",
  1724  			from:  "create table t (id int primary key, i int, key i (i), key i2 (i, id), constraint f foreign key (i) references parent(id))",
  1725  			alter: "alter table t drop key `i2`",
  1726  			to:    "create table t (id int primary key, i int, key i (i), constraint f foreign key (i) references parent(id))",
  1727  		},
  1728  		{
  1729  			name:  "drop key with alternative key for foreign key constraint, 3",
  1730  			from:  "create table t (id int primary key, i int, key i (i), key i2 (i), constraint f foreign key (i) references parent(id))",
  1731  			alter: "alter table t drop key `i`",
  1732  			to:    "create table t (id int primary key, i int, key i2 (i), constraint f foreign key (i) references parent(id))",
  1733  		},
  1734  	}
  1735  	hints := DiffHints{}
  1736  	for _, ts := range tt {
  1737  		t.Run(ts.name, func(t *testing.T) {
  1738  			stmt, err := sqlparser.ParseStrictDDL(ts.from)
  1739  			require.NoError(t, err)
  1740  			fromCreateTable, ok := stmt.(*sqlparser.CreateTable)
  1741  			require.True(t, ok)
  1742  
  1743  			stmt, err = sqlparser.ParseStrictDDL(ts.alter)
  1744  			require.NoError(t, err)
  1745  			alterTable, ok := stmt.(*sqlparser.AlterTable)
  1746  			require.True(t, ok)
  1747  
  1748  			from, err := NewCreateTableEntity(fromCreateTable)
  1749  			require.NoError(t, err)
  1750  			a := &AlterTableEntityDiff{from: from, alterTable: alterTable}
  1751  			applied, err := from.Apply(a)
  1752  			if ts.expectErr != nil {
  1753  				appliedCanonicalStatementString := ""
  1754  				if applied != nil {
  1755  					appliedCanonicalStatementString = applied.Create().CanonicalStatementString()
  1756  				}
  1757  				assert.Error(t, err)
  1758  				assert.EqualErrorf(t, err, ts.expectErr.Error(), "applied: %v", appliedCanonicalStatementString)
  1759  			} else {
  1760  				assert.NoError(t, err)
  1761  				assert.NotNil(t, applied)
  1762  
  1763  				c, ok := applied.(*CreateTableEntity)
  1764  				require.True(t, ok)
  1765  				applied = c.normalize()
  1766  
  1767  				stmt, err := sqlparser.ParseStrictDDL(ts.to)
  1768  				require.NoError(t, err)
  1769  				toCreateTable, ok := stmt.(*sqlparser.CreateTable)
  1770  				require.True(t, ok)
  1771  
  1772  				to, err := NewCreateTableEntity(toCreateTable)
  1773  				require.NoError(t, err)
  1774  				diff, err := applied.Diff(to, &hints)
  1775  				require.NoError(t, err)
  1776  				assert.Empty(t, diff, "diff found: %v.\napplied: %v\nto: %v", diff.CanonicalStatementString(), applied.Create().CanonicalStatementString(), to.Create().CanonicalStatementString())
  1777  			}
  1778  		})
  1779  	}
  1780  }
  1781  
  1782  func TestNormalize(t *testing.T) {
  1783  	tt := []struct {
  1784  		name string
  1785  		from string
  1786  		to   string
  1787  	}{
  1788  		{
  1789  			name: "basic table",
  1790  			from: "create table t (id int, i int, primary key (id))",
  1791  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1792  		},
  1793  		{
  1794  			name: "basic table, primary key",
  1795  			from: "create table t (id int primary key, i int)",
  1796  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1797  		},
  1798  		{
  1799  			name: "removes default null",
  1800  			from: "create table t (id int, i int default null, primary key (id))",
  1801  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1802  		},
  1803  		{
  1804  			name: "keeps not exist",
  1805  			from: "create table if not exists t (id int primary key, i int)",
  1806  			to:   "CREATE TABLE IF NOT EXISTS `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1807  		},
  1808  		{
  1809  			name: "timestamp null",
  1810  			from: "create table t (id int primary key, t timestamp null)",
  1811  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`t` timestamp NULL,\n\tPRIMARY KEY (`id`)\n)",
  1812  		},
  1813  		{
  1814  			name: "timestamp default null",
  1815  			from: "create table t (id int primary key, t timestamp default null)",
  1816  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`t` timestamp NULL,\n\tPRIMARY KEY (`id`)\n)",
  1817  		},
  1818  		{
  1819  			name: "uses lowercase type",
  1820  			from: "create table t (id INT primary key, i INT default null)",
  1821  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1822  		},
  1823  		{
  1824  			name: "removes default signed",
  1825  			from: "create table t (id int signed primary key, i int signed)",
  1826  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1827  		},
  1828  		{
  1829  			name: "does not remove tinyint(1) size",
  1830  			from: "create table t (id int primary key, i tinyint(1) default null)",
  1831  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` tinyint(1),\n\tPRIMARY KEY (`id`)\n)",
  1832  		},
  1833  		{
  1834  			name: "removes other tinyint size",
  1835  			from: "create table t (id int primary key, i tinyint(2) default null)",
  1836  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` tinyint,\n\tPRIMARY KEY (`id`)\n)",
  1837  		},
  1838  		{
  1839  			name: "removes int size",
  1840  			from: "create table t (id int primary key, i int(1) default null)",
  1841  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1842  		},
  1843  		{
  1844  			name: "removes bigint size",
  1845  			from: "create table t (id int primary key, i bigint(1) default null)",
  1846  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` bigint,\n\tPRIMARY KEY (`id`)\n)",
  1847  		},
  1848  		{
  1849  			name: "keeps zerofill",
  1850  			from: "create table t (id int primary key, i int zerofill default null)",
  1851  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int zerofill,\n\tPRIMARY KEY (`id`)\n)",
  1852  		},
  1853  		{
  1854  			name: "removes int sizes case insensitive",
  1855  			from: "create table t (id int primary key, i INT(11) default null)",
  1856  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`)\n)",
  1857  		},
  1858  		{
  1859  			name: "removes float size with correct type",
  1860  			from: "create table t (id int primary key, f float(24) default null)",
  1861  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`f` float,\n\tPRIMARY KEY (`id`)\n)",
  1862  		},
  1863  		{
  1864  			name: "removes float size with correct type",
  1865  			from: "create table t (id int primary key, f float(25) default null)",
  1866  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`f` double,\n\tPRIMARY KEY (`id`)\n)",
  1867  		},
  1868  		{
  1869  			name: "normalizes real type to double",
  1870  			from: "create table t (id int primary key, f real default null)",
  1871  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`f` double,\n\tPRIMARY KEY (`id`)\n)",
  1872  		},
  1873  		{
  1874  			name: "normalizes float4 type to float",
  1875  			from: "create table t (id int primary key, f float4 default null)",
  1876  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`f` float,\n\tPRIMARY KEY (`id`)\n)",
  1877  		},
  1878  		{
  1879  			name: "normalizes float8 type to double",
  1880  			from: "create table t (id int primary key, f float8 default null)",
  1881  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`f` double,\n\tPRIMARY KEY (`id`)\n)",
  1882  		},
  1883  		{
  1884  			name: "removes matching charset",
  1885  			from: "create table t (id int signed primary key, v varchar(255) charset utf8mb4) charset utf8mb4",
  1886  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4",
  1887  		},
  1888  		{
  1889  			name: "removes matching case insensitive charset",
  1890  			from: "create table t (id int signed primary key, v varchar(255) charset UTF8MB4) charset utf8mb4",
  1891  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4",
  1892  		},
  1893  		{
  1894  			name: "removes matching collation if default",
  1895  			from: "create table t (id int signed primary key, v varchar(255) collate utf8mb4_0900_ai_ci) collate utf8mb4_0900_ai_ci",
  1896  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) COLLATE utf8mb4_0900_ai_ci",
  1897  		},
  1898  		{
  1899  			name: "removes matching collation case insensitive if default",
  1900  			from: "create table t (id int signed primary key, v varchar(255) collate UTF8MB4_0900_AI_CI) collate utf8mb4_0900_ai_ci",
  1901  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) COLLATE utf8mb4_0900_ai_ci",
  1902  		},
  1903  		{
  1904  			name: "removes matching charset & collation if default",
  1905  			from: "create table t (id int signed primary key, v varchar(255) charset utf8mb4 collate utf8mb4_0900_ai_ci) charset utf8mb4 collate utf8mb4_0900_ai_ci",
  1906  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_ai_ci",
  1907  		},
  1908  		{
  1909  			name: "sets collation for non default collation at table level",
  1910  			from: "create table t (id int signed primary key, v varchar(255) charset utf8mb4) charset utf8mb4 collate utf8mb4_0900_bin",
  1911  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255) COLLATE utf8mb4_0900_ai_ci,\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_bin",
  1912  		},
  1913  		{
  1914  			name: "does not add collation for a non default collation at table level",
  1915  			from: "create table t (id int signed primary key, v varchar(255)) charset utf8mb4 collate utf8mb4_0900_bin",
  1916  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_bin",
  1917  		},
  1918  		{
  1919  			name: "cleans up collation at the column level if it matches the tabel level and both are given",
  1920  			from: "create table t (id int signed primary key, v varchar(255) collate utf8mb4_0900_bin) charset utf8mb4 collate utf8mb4_0900_bin",
  1921  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_bin",
  1922  		},
  1923  		{
  1924  			name: "cleans up charset and collation at the column level if it matches the tabel level and both are given",
  1925  			from: "create table t (id int signed primary key, v varchar(255) charset utf8mb4 collate utf8mb4_0900_bin) charset utf8mb4 collate utf8mb4_0900_bin",
  1926  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_bin",
  1927  		},
  1928  		{
  1929  			name: "keeps existing collation even if default for non default collation at table level",
  1930  			from: "create table t (id int signed primary key, v varchar(255) charset utf8mb4 collate utf8mb4_0900_ai_ci) charset utf8mb4 collate utf8mb4_0900_bin",
  1931  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255) COLLATE utf8mb4_0900_ai_ci,\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_bin",
  1932  		},
  1933  		{
  1934  			name: "keeps existing collation even if another non default collation",
  1935  			from: "create table t (id int signed primary key, v varchar(255) charset utf8mb4 collate utf8mb4_german2_ci) charset utf8mb4 collate utf8mb4_0900_bin",
  1936  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255) COLLATE utf8mb4_german2_ci,\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_bin",
  1937  		},
  1938  		{
  1939  			name: "maps utf8 to utf8mb3",
  1940  			from: "create table t (id int signed primary key, v varchar(255) charset utf8 collate utf8_general_ci) charset utf8 collate utf8_general_ci",
  1941  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb3,\n  COLLATE utf8mb3_general_ci",
  1942  		},
  1943  		{
  1944  			name: "lowercase table options for charset and collation",
  1945  			from: "create table t (id int signed primary key, v varchar(255) charset utf8 collate utf8_general_ci) charset UTF8 collate UTF8_GENERAL_CI",
  1946  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb3,\n  COLLATE utf8mb3_general_ci",
  1947  		},
  1948  		{
  1949  			name: "drops existing collation if it matches table default at column level for non default charset",
  1950  			from: "create table t (id int signed primary key, v varchar(255) charset utf8mb3 collate utf8_unicode_ci) charset utf8mb3 collate utf8_unicode_ci",
  1951  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`v` varchar(255),\n\tPRIMARY KEY (`id`)\n) CHARSET utf8mb3,\n  COLLATE utf8mb3_unicode_ci",
  1952  		},
  1953  		{
  1954  			name: "correct case table options for engine",
  1955  			from: "create table t (id int signed primary key) engine innodb",
  1956  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\tPRIMARY KEY (`id`)\n) ENGINE InnoDB",
  1957  		},
  1958  		{
  1959  			name: "correct case for engine in partitions",
  1960  			from: "create table a (id int not null primary key) engine InnoDB, charset utf8mb4, collate utf8mb4_0900_ai_ci partition by range (`id`) (partition `p10` values less than(10) engine innodb)",
  1961  			to:   "CREATE TABLE `a` (\n\t`id` int NOT NULL,\n\tPRIMARY KEY (`id`)\n) ENGINE InnoDB,\n  CHARSET utf8mb4,\n  COLLATE utf8mb4_0900_ai_ci\nPARTITION BY RANGE (`id`)\n(PARTITION `p10` VALUES LESS THAN (10) ENGINE InnoDB)",
  1962  		},
  1963  		{
  1964  			name: "generates a name for a key with proper casing",
  1965  			from: "create table t (id int, I int, index i (i), index(I))",
  1966  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`I` int,\n\tKEY `i` (`i`),\n\tKEY `I_2` (`I`)\n)",
  1967  		},
  1968  		{
  1969  			name: "generates a name for checks",
  1970  			from: "create table t (id int NOT NULL, test int NOT NULL DEFAULT 0, PRIMARY KEY (id), CHECK ((test >= 0)))",
  1971  			to:   "CREATE TABLE `t` (\n\t`id` int NOT NULL,\n\t`test` int NOT NULL DEFAULT 0,\n\tPRIMARY KEY (`id`),\n\tCONSTRAINT `t_chk_1` CHECK (`test` >= 0)\n)",
  1972  		},
  1973  		{
  1974  			name: "generates a name for checks with proper casing",
  1975  			from: "create table t (id int NOT NULL, test int NOT NULL DEFAULT 0, PRIMARY KEY (id), CONSTRAINT t_CHK_1 CHECK (test >= 0), CHECK ((test >= 0)))",
  1976  			to:   "CREATE TABLE `t` (\n\t`id` int NOT NULL,\n\t`test` int NOT NULL DEFAULT 0,\n\tPRIMARY KEY (`id`),\n\tCONSTRAINT `t_CHK_1` CHECK (`test` >= 0),\n\tCONSTRAINT `t_chk_2` CHECK (`test` >= 0)\n)",
  1977  		},
  1978  		{
  1979  			name: "generates a name for foreign key constraints",
  1980  			from: "create table t1 (id int primary key, i int, key i_idx (i), foreign key (i) references parent(id))",
  1981  			to:   "CREATE TABLE `t1` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i_idx` (`i`),\n\tCONSTRAINT `t1_ibfk_1` FOREIGN KEY (`i`) REFERENCES `parent` (`id`)\n)",
  1982  		},
  1983  		{
  1984  			name: "creates an index for foreign key constraints",
  1985  			from: "create table t1 (id int primary key, i int, constraint f foreign key (i) references parent(id))",
  1986  			to:   "CREATE TABLE `t1` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`),\n\tKEY `f` (`i`),\n\tCONSTRAINT `f` FOREIGN KEY (`i`) REFERENCES `parent` (`id`)\n)",
  1987  		},
  1988  		{
  1989  			name: "creates an index for unnamed foreign key constraints",
  1990  			from: "create table t1 (id int primary key, i int, foreign key (i) references parent(id))",
  1991  			to:   "CREATE TABLE `t1` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i` (`i`),\n\tCONSTRAINT `t1_ibfk_1` FOREIGN KEY (`i`) REFERENCES `parent` (`id`)\n)",
  1992  		},
  1993  		{
  1994  			name: "does not add index since one already defined for foreign key constraint",
  1995  			from: "create table t1 (id int primary key, i int, key i_idx (i), foreign key (i) references parent(id))",
  1996  			to:   "CREATE TABLE `t1` (\n\t`id` int,\n\t`i` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i_idx` (`i`),\n\tCONSTRAINT `t1_ibfk_1` FOREIGN KEY (`i`) REFERENCES `parent` (`id`)\n)",
  1997  		},
  1998  		{
  1999  			name: "uses KEY for indexes",
  2000  			from: "create table t (id int primary key, i1 int, index i1_idx(i1))",
  2001  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i1` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i1_idx` (`i1`)\n)",
  2002  		},
  2003  		{
  2004  			name: "drops default index type",
  2005  			from: "create table t (id int primary key, i1 int, key i1_idx(i1) using btree)",
  2006  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i1` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i1_idx` (`i1`)\n)",
  2007  		},
  2008  		{
  2009  			name: "does not drop non-default index type",
  2010  			from: "create table t (id int primary key, i1 int, key i1_idx(i1) using hash)",
  2011  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i1` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i1_idx` (`i1`) USING hash\n)",
  2012  		},
  2013  		{
  2014  			name: "drops default index visibility",
  2015  			from: "create table t (id int primary key, i1 int, key i1_idx(i1) visible)",
  2016  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i1` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i1_idx` (`i1`)\n)",
  2017  		},
  2018  		{
  2019  			name: "drops non-default index visibility",
  2020  			from: "create table t (id int primary key, i1 int, key i1_idx(i1) invisible)",
  2021  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i1` int,\n\tPRIMARY KEY (`id`),\n\tKEY `i1_idx` (`i1`) INVISIBLE\n)",
  2022  		},
  2023  		{
  2024  			name: "drops default column visibility",
  2025  			from: "create table t (id int primary key, i1 int visible)",
  2026  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i1` int,\n\tPRIMARY KEY (`id`)\n)",
  2027  		},
  2028  		{
  2029  			name: "drops non-default column visibility",
  2030  			from: "create table t (id int primary key, i1 int invisible)",
  2031  			to:   "CREATE TABLE `t` (\n\t`id` int,\n\t`i1` int INVISIBLE,\n\tPRIMARY KEY (`id`)\n)",
  2032  		},
  2033  	}
  2034  	for _, ts := range tt {
  2035  		t.Run(ts.name, func(t *testing.T) {
  2036  			stmt, err := sqlparser.ParseStrictDDL(ts.from)
  2037  			require.NoError(t, err)
  2038  			fromCreateTable, ok := stmt.(*sqlparser.CreateTable)
  2039  			require.True(t, ok)
  2040  
  2041  			from, err := NewCreateTableEntity(fromCreateTable)
  2042  			require.NoError(t, err)
  2043  			assert.Equal(t, ts.to, sqlparser.CanonicalString(from))
  2044  		})
  2045  	}
  2046  }
  2047  
  2048  func TestIndexesCoveringForeignKeyColumns(t *testing.T) {
  2049  	sql := `
  2050  		create table t (
  2051  			id int,
  2052  			a int,
  2053  			b int,
  2054  			c int,
  2055  			d int,
  2056  			e int,
  2057  			z int,
  2058  			primary key (id),
  2059  			key ax (a),
  2060  			key abx (a, b),
  2061  			key bx (b),
  2062  			key bax (b, a),
  2063  			key abcdx (a, b, c, d),
  2064  			key dex (d, e)
  2065  		)
  2066  	`
  2067  	tt := []struct {
  2068  		columns []string
  2069  		indexes []string
  2070  	}{
  2071  		{},
  2072  		{
  2073  			columns: []string{"a"},
  2074  			indexes: []string{"ax", "abx", "abcdx"},
  2075  		},
  2076  		{
  2077  			columns: []string{"b"},
  2078  			indexes: []string{"bx", "bax"},
  2079  		},
  2080  		{
  2081  			columns: []string{"c"},
  2082  		},
  2083  		{
  2084  			columns: []string{"d"},
  2085  			indexes: []string{"dex"},
  2086  		},
  2087  		{
  2088  			columns: []string{"e"},
  2089  		},
  2090  		{
  2091  			columns: []string{"z"},
  2092  		},
  2093  		{
  2094  			columns: []string{"a", "b"},
  2095  			indexes: []string{"abx", "abcdx"},
  2096  		},
  2097  		{
  2098  			columns: []string{"A", "B"},
  2099  			indexes: []string{"abx", "abcdx"},
  2100  		},
  2101  		{
  2102  			columns: []string{"a", "b", "c"},
  2103  			indexes: []string{"abcdx"},
  2104  		},
  2105  		{
  2106  			columns: []string{"a", "b", "c", "d"},
  2107  			indexes: []string{"abcdx"},
  2108  		},
  2109  		{
  2110  			columns: []string{"a", "b", "c", "d", "e"},
  2111  		},
  2112  		{
  2113  			columns: []string{"b", "a"},
  2114  			indexes: []string{"bax"},
  2115  		},
  2116  		{
  2117  			columns: []string{"d", "e"},
  2118  			indexes: []string{"dex"},
  2119  		},
  2120  		{
  2121  			columns: []string{"a", "e"},
  2122  		},
  2123  	}
  2124  
  2125  	stmt, err := sqlparser.ParseStrictDDL(sql)
  2126  	require.NoError(t, err)
  2127  	createTable, ok := stmt.(*sqlparser.CreateTable)
  2128  	require.True(t, ok)
  2129  	c, err := NewCreateTableEntity(createTable)
  2130  	require.NoError(t, err)
  2131  	tableColumns := map[string]sqlparser.IdentifierCI{}
  2132  	for _, col := range c.CreateTable.TableSpec.Columns {
  2133  		tableColumns[col.Name.Lowered()] = col.Name
  2134  	}
  2135  	for _, ts := range tt {
  2136  		name := strings.Join(ts.columns, ",")
  2137  		t.Run(name, func(t *testing.T) {
  2138  			columns := sqlparser.Columns{}
  2139  			for _, colName := range ts.columns {
  2140  				col, ok := tableColumns[strings.ToLower(colName)]
  2141  				require.True(t, ok)
  2142  				columns = append(columns, col)
  2143  			}
  2144  
  2145  			indexes := c.indexesCoveringForeignKeyColumns(columns)
  2146  			var indexesNames []string
  2147  			for _, index := range indexes {
  2148  				indexesNames = append(indexesNames, index.Info.Name.String())
  2149  			}
  2150  			assert.Equal(t, ts.indexes, indexesNames)
  2151  		})
  2152  	}
  2153  }