vitess.io/vitess@v0.16.2/go/vt/vttablet/onlineddl/executor_test.go (about)

     1  /*
     2  Copyright 2021 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  /*
    18  Functionality of this Executor is tested in go/test/endtoend/onlineddl/...
    19  */
    20  
    21  package onlineddl
    22  
    23  import (
    24  	"context"
    25  	"testing"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"vitess.io/vitess/go/vt/schema"
    31  	"vitess.io/vitess/go/vt/sqlparser"
    32  )
    33  
    34  func TestVexecUpdateTemplates(t *testing.T) {
    35  	{
    36  		match, err := sqlparser.QueryMatchesTemplates("select 1 from dual", vexecUpdateTemplates)
    37  		assert.NoError(t, err)
    38  		assert.False(t, match)
    39  	}
    40  	queries := []string{
    41  		`update _vt.schema_migrations set migration_status='cancel-all' where mysql_schema='vt_commerce'`,
    42  		`update _vt.schema_migrations set migration_status = 'cancel-all' where migration_uuid='a5a563da_dc1a_11ec_a416_0a43f95f28a3' and mysql_schema = 'vt_commerce'`,
    43  		`update _vt.schema_migrations set migration_status = 'cancel-all' where migration_uuid='a5a563da_dc1a_11ec_a416_0a43f95f28a3' and mysql_schema = 'vt_commerce' and shard='0'`,
    44  	}
    45  	for _, query := range queries {
    46  		t.Run(query, func(t *testing.T) {
    47  			match, err := sqlparser.QueryMatchesTemplates(query, vexecUpdateTemplates)
    48  			assert.NoError(t, err)
    49  			assert.True(t, match)
    50  		})
    51  	}
    52  }
    53  
    54  func TestGetConstraintType(t *testing.T) {
    55  	{
    56  		typ := GetConstraintType(&sqlparser.CheckConstraintDefinition{})
    57  		assert.Equal(t, CheckConstraintType, typ)
    58  	}
    59  	{
    60  		typ := GetConstraintType(&sqlparser.ForeignKeyDefinition{})
    61  		assert.Equal(t, ForeignKeyConstraintType, typ)
    62  	}
    63  }
    64  
    65  func TestValidateAndEditCreateTableStatement(t *testing.T) {
    66  	e := Executor{}
    67  	tt := []struct {
    68  		name             string
    69  		query            string
    70  		strategyOptions  string
    71  		expectError      string
    72  		countConstraints int
    73  	}{
    74  		{
    75  			name: "table with FK, not allowed",
    76  			query: `
    77  				create table onlineddl_test (
    78  						id int auto_increment,
    79  						i int not null,
    80  						parent_id int not null,
    81  						primary key(id),
    82  						constraint test_fk foreign key (parent_id) references onlineddl_test_parent (id) on delete no action
    83  					)
    84  				`,
    85  			countConstraints: 1,
    86  			expectError:      schema.ErrForeignKeyFound.Error(),
    87  		},
    88  		{
    89  			name: "table with FK, allowed",
    90  			query: `
    91  				create table onlineddl_test (
    92  						id int auto_increment,
    93  						i int not null,
    94  						parent_id int not null,
    95  						primary key(id),
    96  						constraint test_fk foreign key (parent_id) references onlineddl_test_parent (id) on delete no action
    97  					)
    98  				`,
    99  			strategyOptions:  "--unsafe-allow-foreign-keys",
   100  			countConstraints: 1,
   101  		},
   102  		{
   103  			name: "table with anonymous FK, allowed",
   104  			query: `
   105  				create table onlineddl_test (
   106  						id int auto_increment,
   107  						i int not null,
   108  						parent_id int not null,
   109  						primary key(id),
   110  						foreign key (parent_id) references onlineddl_test_parent (id) on delete no action
   111  					)
   112  				`,
   113  			strategyOptions:  "--unsafe-allow-foreign-keys",
   114  			countConstraints: 1,
   115  		},
   116  		{
   117  			name: "table with CHECK constraints",
   118  			query: `
   119  				create table onlineddl_test (
   120  						id int auto_increment,
   121  						i int not null,
   122  						primary key(id),
   123  						constraint check_1 CHECK ((i >= 0)),
   124  						constraint check_2 CHECK ((i <> 5)),
   125  						constraint check_3 CHECK ((i >= 0)),
   126  						constraint chk_1111033c1d2d5908bf1f956ba900b192_check_4 CHECK ((i >= 0))
   127  					)
   128  				`,
   129  			countConstraints: 4,
   130  		},
   131  		{
   132  			name: "table with both FOREIGN and CHECK constraints",
   133  			query: `
   134  				create table onlineddl_test (
   135  						id int auto_increment,
   136  						i int not null,
   137  						primary key(id),
   138  						constraint check_1 CHECK ((i >= 0)),
   139  						constraint test_fk foreign key (parent_id) references onlineddl_test_parent (id) on delete no action,
   140  						constraint chk_1111033c1d2d5908bf1f956ba900b192_check_4 CHECK ((i >= 0))
   141  					)
   142  				`,
   143  			strategyOptions:  "--unsafe-allow-foreign-keys",
   144  			countConstraints: 3,
   145  		},
   146  	}
   147  	for _, tc := range tt {
   148  		t.Run(tc.name, func(t *testing.T) {
   149  			stmt, err := sqlparser.ParseStrictDDL(tc.query)
   150  			require.NoError(t, err)
   151  			createTable, ok := stmt.(*sqlparser.CreateTable)
   152  			require.True(t, ok)
   153  
   154  			onlineDDL := &schema.OnlineDDL{UUID: "a5a563da_dc1a_11ec_a416_0a43f95f28a3", Table: "onlineddl_test", Options: tc.strategyOptions}
   155  			constraintMap, err := e.validateAndEditCreateTableStatement(context.Background(), onlineDDL, createTable)
   156  			if tc.expectError != "" {
   157  				require.Error(t, err)
   158  				assert.ErrorContains(t, err, tc.expectError)
   159  			} else {
   160  				assert.NoError(t, err)
   161  			}
   162  			uniqueConstraintNames := map[string]bool{}
   163  			err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
   164  				switch node := node.(type) {
   165  				case *sqlparser.ConstraintDefinition:
   166  					uniqueConstraintNames[node.Name.String()] = true
   167  				}
   168  				return true, nil
   169  			}, createTable)
   170  			assert.NoError(t, err)
   171  			assert.Equal(t, tc.countConstraints, len(uniqueConstraintNames))
   172  			assert.Equalf(t, tc.countConstraints, len(constraintMap), "got contraints: %v", constraintMap)
   173  		})
   174  	}
   175  }
   176  
   177  func TestValidateAndEditAlterTableStatement(t *testing.T) {
   178  	e := Executor{}
   179  	tt := []struct {
   180  		alter  string
   181  		expect []string
   182  	}{
   183  		{
   184  			alter:  "alter table t add column i int",
   185  			expect: []string{"alter table t add column i int, algorithm = copy"},
   186  		},
   187  		{
   188  			alter:  "alter table t add column i int, add fulltext key name1_ft (name1)",
   189  			expect: []string{"alter table t add column i int, add fulltext key name1_ft (name1), algorithm = copy"},
   190  		},
   191  		{
   192  			alter:  "alter table t add column i int, add fulltext key name1_ft (name1), add fulltext key name2_ft (name2)",
   193  			expect: []string{"alter table t add column i int, add fulltext key name1_ft (name1), algorithm = copy", "alter table t add fulltext key name2_ft (name2), algorithm = copy"},
   194  		},
   195  		{
   196  			alter:  "alter table t add fulltext key name0_ft (name0), add column i int, add fulltext key name1_ft (name1), add fulltext key name2_ft (name2)",
   197  			expect: []string{"alter table t add fulltext key name0_ft (name0), add column i int, algorithm = copy", "alter table t add fulltext key name1_ft (name1), algorithm = copy", "alter table t add fulltext key name2_ft (name2), algorithm = copy"},
   198  		},
   199  		{
   200  			alter:  "alter table t add constraint check (id != 1)",
   201  			expect: []string{"alter table t add constraint chk_aulpn7bjeortljhguy86phdn9 check (id != 1), algorithm = copy"},
   202  		},
   203  		{
   204  			alter:  "alter table t add constraint t_chk_1 check (id != 1)",
   205  			expect: []string{"alter table t add constraint chk_1_aulpn7bjeortljhguy86phdn9 check (id != 1), algorithm = copy"},
   206  		},
   207  		{
   208  			alter:  "alter table t add constraint some_check check (id != 1)",
   209  			expect: []string{"alter table t add constraint some_check_aulpn7bjeortljhguy86phdn9 check (id != 1), algorithm = copy"},
   210  		},
   211  		{
   212  			alter:  "alter table t add constraint some_check check (id != 1), add constraint another_check check (id != 2)",
   213  			expect: []string{"alter table t add constraint some_check_aulpn7bjeortljhguy86phdn9 check (id != 1), add constraint another_check_4fa197273p3w96267pzm3gfi3 check (id != 2), algorithm = copy"},
   214  		},
   215  		{
   216  			alter:  "alter table t add foreign key (parent_id) references onlineddl_test_parent (id) on delete no action",
   217  			expect: []string{"alter table t add constraint fk_6fmhzdlya89128u5j3xapq34i foreign key (parent_id) references onlineddl_test_parent (id) on delete no action, algorithm = copy"},
   218  		},
   219  		{
   220  			alter:  "alter table t add constraint myfk foreign key (parent_id) references onlineddl_test_parent (id) on delete no action",
   221  			expect: []string{"alter table t add constraint myfk_6fmhzdlya89128u5j3xapq34i foreign key (parent_id) references onlineddl_test_parent (id) on delete no action, algorithm = copy"},
   222  		},
   223  		{
   224  			alter:  "alter table t add constraint t_fk_1 foreign key (parent_id) references onlineddl_test_parent (id) on delete no action",
   225  			expect: []string{"alter table t add constraint fk_1_6fmhzdlya89128u5j3xapq34i foreign key (parent_id) references onlineddl_test_parent (id) on delete no action, algorithm = copy"},
   226  		},
   227  		{
   228  			alter:  "alter table t add constraint t_fk_1 foreign key (parent_id) references onlineddl_test_parent (id) on delete no action, add constraint some_check check (id != 1)",
   229  			expect: []string{"alter table t add constraint fk_1_6fmhzdlya89128u5j3xapq34i foreign key (parent_id) references onlineddl_test_parent (id) on delete no action, add constraint some_check_aulpn7bjeortljhguy86phdn9 check (id != 1), algorithm = copy"},
   230  		},
   231  	}
   232  	for _, tc := range tt {
   233  		t.Run(tc.alter, func(t *testing.T) {
   234  			stmt, err := sqlparser.ParseStrictDDL(tc.alter)
   235  			require.NoError(t, err)
   236  			alterTable, ok := stmt.(*sqlparser.AlterTable)
   237  			require.True(t, ok)
   238  
   239  			m := map[string]string{}
   240  			onlineDDL := &schema.OnlineDDL{UUID: "a5a563da_dc1a_11ec_a416_0a43f95f28a3", Table: "t", Options: "--unsafe-allow-foreign-keys"}
   241  			alters, err := e.validateAndEditAlterTableStatement(context.Background(), onlineDDL, alterTable, m)
   242  			assert.NoError(t, err)
   243  			altersStrings := []string{}
   244  			for _, alter := range alters {
   245  				altersStrings = append(altersStrings, sqlparser.String(alter))
   246  			}
   247  			assert.Equal(t, tc.expect, altersStrings)
   248  		})
   249  	}
   250  }
   251  
   252  func TestAddInstantAlgorithm(t *testing.T) {
   253  	e := Executor{}
   254  	tt := []struct {
   255  		alter  string
   256  		expect string
   257  	}{
   258  		{
   259  			alter:  "alter table t add column i2 int not null",
   260  			expect: "ALTER TABLE `t` ADD COLUMN `i2` int NOT NULL, ALGORITHM = INSTANT",
   261  		},
   262  		{
   263  			alter:  "alter table t add column i2 int not null, lock=none",
   264  			expect: "ALTER TABLE `t` ADD COLUMN `i2` int NOT NULL, LOCK NONE, ALGORITHM = INSTANT",
   265  		},
   266  		{
   267  			alter:  "alter table t add column i2 int not null, algorithm=inplace",
   268  			expect: "ALTER TABLE `t` ADD COLUMN `i2` int NOT NULL, ALGORITHM = INSTANT",
   269  		},
   270  		{
   271  			alter:  "alter table t add column i2 int not null, algorithm=inplace, lock=none",
   272  			expect: "ALTER TABLE `t` ADD COLUMN `i2` int NOT NULL, ALGORITHM = INSTANT, LOCK NONE",
   273  		},
   274  	}
   275  	for _, tc := range tt {
   276  		t.Run(tc.alter, func(t *testing.T) {
   277  			stmt, err := sqlparser.ParseStrictDDL(tc.alter)
   278  			require.NoError(t, err)
   279  			alterTable, ok := stmt.(*sqlparser.AlterTable)
   280  			require.True(t, ok)
   281  
   282  			e.addInstantAlgorithm(alterTable)
   283  			alterInstant := sqlparser.CanonicalString(alterTable)
   284  
   285  			assert.Equal(t, tc.expect, alterInstant)
   286  
   287  			stmt, err = sqlparser.ParseStrictDDL(alterInstant)
   288  			require.NoError(t, err)
   289  			_, ok = stmt.(*sqlparser.AlterTable)
   290  			require.True(t, ok)
   291  		})
   292  	}
   293  }