vitess.io/vitess@v0.16.2/go/vt/schemamanager/tablet_executor_test.go (about) 1 /* 2 Copyright 2019 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 schemamanager 18 19 import ( 20 "context" 21 "strings" 22 "testing" 23 "time" 24 25 "github.com/stretchr/testify/assert" 26 27 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 28 "vitess.io/vitess/go/vt/topo/memorytopo" 29 30 "vitess.io/vitess/go/vt/logutil" 31 "vitess.io/vitess/go/vt/mysqlctl/tmutils" 32 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 33 "vitess.io/vitess/go/vt/schema" 34 "vitess.io/vitess/go/vt/sqlparser" 35 ) 36 37 var ( 38 testWaitReplicasTimeout = 10 * time.Second 39 ) 40 41 func TestTabletExecutorOpen(t *testing.T) { 42 executor := newFakeExecutor(t) 43 ctx := context.Background() 44 45 if err := executor.Open(ctx, "test_keyspace"); err != nil { 46 t.Fatalf("executor.Open should succeed") 47 } 48 49 defer executor.Close() 50 51 if err := executor.Open(ctx, "test_keyspace"); err != nil { 52 t.Fatalf("open an opened executor should also succeed") 53 } 54 } 55 56 func TestTabletExecutorOpenWithEmptyPrimaryAlias(t *testing.T) { 57 ctx := context.Background() 58 ts := memorytopo.NewServer("test_cell") 59 tablet := &topodatapb.Tablet{ 60 Alias: &topodatapb.TabletAlias{ 61 Cell: "test_cell", 62 Uid: 1, 63 }, 64 Keyspace: "test_keyspace", 65 Shard: "0", 66 Type: topodatapb.TabletType_REPLICA, 67 } 68 // This will create the Keyspace, Shard and Tablet record. 69 // Since this is a replica tablet, the Shard will have no primary. 70 if err := ts.InitTablet(ctx, tablet, false /*allowPrimaryOverride*/, true /*createShardAndKeyspace*/, false /*allowUpdate*/); err != nil { 71 t.Fatalf("InitTablet failed: %v", err) 72 } 73 executor := NewTabletExecutor("TestTabletExecutorOpenWithEmptyPrimaryAlias", ts, newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout) 74 if err := executor.Open(ctx, "test_keyspace"); err == nil || !strings.Contains(err.Error(), "does not have a primary") { 75 t.Fatalf("executor.Open() = '%v', want error", err) 76 } 77 executor.Close() 78 } 79 80 func TestTabletExecutorValidate(t *testing.T) { 81 fakeTmc := newFakeTabletManagerClient() 82 83 fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{ 84 DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */", 85 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 86 { 87 Name: "test_table", 88 Schema: "table schema", 89 Type: tmutils.TableBaseTable, 90 }, 91 { 92 Name: "test_table_03", 93 Schema: "table schema", 94 Type: tmutils.TableBaseTable, 95 RowCount: 200000, 96 }, 97 { 98 Name: "test_table_04", 99 Schema: "table schema", 100 Type: tmutils.TableBaseTable, 101 RowCount: 3000000, 102 }, 103 }, 104 }) 105 106 executor := NewTabletExecutor("TestTabletExecutorValidate", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout) 107 ctx := context.Background() 108 109 sqls := []string{ 110 "ALTER TABLE test_table ADD COLUMN new_id bigint(20)", 111 "CREATE TABLE test_table_02 (pk int)", 112 "ALTER DATABASE db_name DEFAULT CHARACTER SET = utf8mb4", 113 "ALTER SCHEMA db_name CHARACTER SET = utf8mb4", 114 } 115 116 if err := executor.Validate(ctx, sqls); err == nil { 117 t.Fatalf("validate should fail because executor is closed") 118 } 119 120 executor.Open(ctx, "test_keyspace") 121 defer executor.Close() 122 123 // schema changes with DMLs should fail 124 if err := executor.Validate(ctx, []string{ 125 "INSERT INTO test_table VALUES(1)"}); err == nil { 126 t.Fatalf("schema changes are for DDLs") 127 } 128 129 // validates valid ddls 130 if err := executor.Validate(ctx, sqls); err != nil { 131 t.Fatalf("executor.Validate should succeed, but got error: %v", err) 132 } 133 134 // alter a table with more than 100,000 rows 135 if err := executor.Validate(ctx, []string{ 136 "ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)", 137 }); err == nil { 138 t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows") 139 } 140 141 if err := executor.Validate(ctx, []string{ 142 "TRUNCATE TABLE test_table_04", 143 }); err != nil { 144 t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed") 145 } 146 147 if err := executor.Validate(ctx, []string{ 148 "DROP TABLE test_table_04", 149 }); err != nil { 150 t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed") 151 } 152 153 executor.AllowBigSchemaChange() 154 // alter a table with more than 100,000 rows 155 if err := executor.Validate(ctx, []string{ 156 "ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)", 157 }); err != nil { 158 t.Fatalf("executor.Validate should succeed, big schema change is disabled") 159 } 160 161 executor.DisallowBigSchemaChange() 162 if err := executor.Validate(ctx, []string{ 163 "ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)", 164 }); err == nil { 165 t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows") 166 } 167 } 168 169 func TestTabletExecutorDML(t *testing.T) { 170 fakeTmc := newFakeTabletManagerClient() 171 172 fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{ 173 DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */", 174 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 175 { 176 Name: "test_table", 177 Schema: "table schema", 178 Type: tmutils.TableBaseTable, 179 }, 180 { 181 Name: "test_table_03", 182 Schema: "table schema", 183 Type: tmutils.TableBaseTable, 184 RowCount: 200000, 185 }, 186 { 187 Name: "test_table_04", 188 Schema: "table schema", 189 Type: tmutils.TableBaseTable, 190 RowCount: 3000000, 191 }, 192 }, 193 }) 194 195 executor := NewTabletExecutor("TestTabletExecutorDML", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout) 196 ctx := context.Background() 197 198 executor.Open(ctx, "unsharded_keyspace") 199 defer executor.Close() 200 201 // schema changes with DMLs should fail 202 if err := executor.Validate(ctx, []string{ 203 "INSERT INTO test_table VALUES(1)"}); err != nil { 204 t.Fatalf("executor.Validate should succeed, for DML to unsharded keyspace") 205 } 206 } 207 208 func TestTabletExecutorExecute(t *testing.T) { 209 executor := newFakeExecutor(t) 210 ctx := context.Background() 211 212 sqls := []string{"DROP TABLE unknown_table"} 213 214 result := executor.Execute(ctx, sqls) 215 if result.ExecutorErr == "" { 216 t.Fatalf("execute should fail, call execute.Open first") 217 } 218 } 219 220 func TestIsOnlineSchemaDDL(t *testing.T) { 221 tt := []struct { 222 query string 223 ddlStrategy string 224 isOnlineDDL bool 225 strategy schema.DDLStrategy 226 options string 227 }{ 228 { 229 query: "CREATE TABLE t(id int)", 230 isOnlineDDL: false, 231 }, 232 { 233 query: "CREATE TABLE t(id int)", 234 ddlStrategy: "gh-ost", 235 isOnlineDDL: true, 236 strategy: schema.DDLStrategyGhost, 237 }, 238 { 239 query: "ALTER TABLE t ADD COLUMN i INT", 240 ddlStrategy: "online", 241 isOnlineDDL: true, 242 strategy: schema.DDLStrategyOnline, 243 }, 244 { 245 query: "ALTER TABLE t ADD COLUMN i INT", 246 ddlStrategy: "vitess", 247 isOnlineDDL: true, 248 strategy: schema.DDLStrategyVitess, 249 }, 250 { 251 query: "ALTER TABLE t ADD COLUMN i INT", 252 ddlStrategy: "", 253 isOnlineDDL: false, 254 }, 255 { 256 query: "ALTER TABLE t ADD COLUMN i INT", 257 ddlStrategy: "gh-ost", 258 isOnlineDDL: true, 259 strategy: schema.DDLStrategyGhost, 260 }, 261 { 262 query: "ALTER TABLE t ADD COLUMN i INT", 263 ddlStrategy: "gh-ost --max-load=Threads_running=100", 264 isOnlineDDL: true, 265 strategy: schema.DDLStrategyGhost, 266 options: "--max-load=Threads_running=100", 267 }, 268 { 269 query: "TRUNCATE TABLE t", 270 ddlStrategy: "online", 271 isOnlineDDL: false, 272 }, 273 { 274 query: "TRUNCATE TABLE t", 275 ddlStrategy: "gh-ost", 276 isOnlineDDL: false, 277 }, 278 { 279 query: "RENAME TABLE t to t2", 280 ddlStrategy: "gh-ost", 281 isOnlineDDL: false, 282 }, 283 } 284 285 for _, ts := range tt { 286 e := &TabletExecutor{} 287 err := e.SetDDLStrategy(ts.ddlStrategy) 288 assert.NoError(t, err) 289 290 stmt, err := sqlparser.Parse(ts.query) 291 assert.NoError(t, err) 292 293 ddlStmt, ok := stmt.(sqlparser.DDLStatement) 294 assert.True(t, ok) 295 296 isOnlineDDL := e.isOnlineSchemaDDL(ddlStmt) 297 assert.Equal(t, ts.isOnlineDDL, isOnlineDDL) 298 if isOnlineDDL { 299 assert.Equal(t, ts.strategy, e.ddlStrategySetting.Strategy) 300 assert.Equal(t, ts.options, e.ddlStrategySetting.Options) 301 } 302 } 303 }