github.com/iasthc/atlas/cmd/atlas@v0.0.0-20230523071841-73246df3f88d/internal/sqlparse/myparse/myparse_test.go (about) 1 // Copyright 2021-present The Atlas Authors. All rights reserved. 2 // This source code is licensed under the Apache 2.0 license found 3 // in the LICENSE file in the root directory of this source tree. 4 5 package myparse_test 6 7 import ( 8 "strconv" 9 "testing" 10 11 "github.com/iasthc/atlas/cmd/atlas/internal/sqlparse/myparse" 12 "github.com/iasthc/atlas/sql/migrate" 13 "github.com/iasthc/atlas/sql/schema" 14 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestFixChange_RenameColumns(t *testing.T) { 19 var p myparse.Parser 20 _, err := p.FixChange( 21 nil, 22 "ALTER TABLE t RENAME COLUMN c1 TO c2", 23 schema.Changes{&schema.AddTable{}}, 24 ) 25 require.Error(t, err) 26 27 changes, err := p.FixChange( 28 nil, 29 "ALTER TABLE t RENAME COLUMN c1 TO c2", 30 schema.Changes{ 31 &schema.ModifyTable{ 32 Changes: schema.Changes{ 33 &schema.DropColumn{C: schema.NewColumn("c1")}, 34 &schema.AddColumn{C: schema.NewColumn("c2")}, 35 }, 36 }, 37 }, 38 ) 39 require.NoError(t, err) 40 require.Equal( 41 t, 42 schema.Changes{ 43 &schema.ModifyTable{ 44 Changes: schema.Changes{ 45 &schema.RenameColumn{From: schema.NewColumn("c1"), To: schema.NewColumn("c2")}, 46 }, 47 }, 48 }, 49 changes, 50 ) 51 52 changes, err = p.FixChange( 53 nil, 54 "ALTER TABLE t ADD INDEX i(id), RENAME COLUMN c1 TO c2, ADD COLUMN c3 int, DROP COLUMN c4", 55 schema.Changes{ 56 &schema.ModifyTable{ 57 Changes: schema.Changes{ 58 &schema.AddIndex{I: schema.NewIndex("i").AddColumns(schema.NewColumn("id"))}, 59 &schema.DropColumn{C: schema.NewColumn("c1")}, 60 &schema.AddColumn{C: schema.NewColumn("c2")}, 61 &schema.AddColumn{C: schema.NewColumn("c3")}, 62 &schema.AddColumn{C: schema.NewColumn("c4")}, 63 }, 64 }, 65 }, 66 ) 67 require.NoError(t, err) 68 require.Equal( 69 t, 70 schema.Changes{ 71 &schema.ModifyTable{ 72 Changes: schema.Changes{ 73 &schema.AddIndex{I: schema.NewIndex("i").AddColumns(schema.NewColumn("id"))}, 74 &schema.RenameColumn{From: schema.NewColumn("c1"), To: schema.NewColumn("c2")}, 75 &schema.AddColumn{C: schema.NewColumn("c3")}, 76 &schema.AddColumn{C: schema.NewColumn("c4")}, 77 }, 78 }, 79 }, 80 changes, 81 ) 82 } 83 84 func TestFixChange_RenameIndexes(t *testing.T) { 85 var p myparse.Parser 86 changes, err := p.FixChange( 87 nil, 88 "ALTER TABLE t RENAME Index i1 TO i2", 89 schema.Changes{ 90 &schema.ModifyTable{ 91 Changes: schema.Changes{ 92 &schema.DropIndex{I: schema.NewIndex("i1")}, 93 &schema.AddIndex{I: schema.NewIndex("i2")}, 94 }, 95 }, 96 }, 97 ) 98 require.NoError(t, err) 99 require.Equal( 100 t, 101 schema.Changes{ 102 &schema.ModifyTable{ 103 Changes: schema.Changes{ 104 &schema.RenameIndex{From: schema.NewIndex("i1"), To: schema.NewIndex("i2")}, 105 }, 106 }, 107 }, 108 changes, 109 ) 110 } 111 112 func TestFixChange_RenameTable(t *testing.T) { 113 var p myparse.Parser 114 changes, err := p.FixChange( 115 nil, 116 "RENAME TABLE t1 TO t2", 117 schema.Changes{ 118 &schema.DropTable{T: schema.NewTable("t1")}, 119 &schema.AddTable{T: schema.NewTable("t2")}, 120 }, 121 ) 122 require.NoError(t, err) 123 require.Equal( 124 t, 125 schema.Changes{ 126 &schema.RenameTable{From: schema.NewTable("t1"), To: schema.NewTable("t2")}, 127 }, 128 changes, 129 ) 130 changes, err = p.FixChange( 131 nil, 132 "RENAME TABLE `s1`.`t1` TO `s1`.`t2`;", 133 schema.Changes{ 134 &schema.DropTable{T: schema.NewTable("t1")}, 135 &schema.AddTable{T: schema.NewTable("t2")}, 136 }, 137 ) 138 require.NoError(t, err) 139 require.Equal( 140 t, 141 schema.Changes{ 142 &schema.RenameTable{From: schema.NewTable("t1"), To: schema.NewTable("t2")}, 143 }, 144 changes, 145 ) 146 changes, err = p.FixChange( 147 nil, 148 "RENAME TABLE t1 TO t2, t3 TO t4", 149 schema.Changes{ 150 &schema.DropTable{T: schema.NewTable("t1")}, 151 &schema.AddTable{T: schema.NewTable("t2")}, 152 &schema.DropTable{T: schema.NewTable("t3")}, 153 &schema.AddTable{T: schema.NewTable("t4")}, 154 }, 155 ) 156 require.NoError(t, err) 157 require.Equal( 158 t, 159 schema.Changes{ 160 &schema.RenameTable{From: schema.NewTable("t1"), To: schema.NewTable("t2")}, 161 &schema.RenameTable{From: schema.NewTable("t3"), To: schema.NewTable("t4")}, 162 }, 163 changes, 164 ) 165 } 166 167 func TestFixChange_AlterAndRename(t *testing.T) { 168 var ( 169 p myparse.Parser 170 drv = &mockDriver{} 171 ) 172 drv.changes = append(drv.changes, &schema.AddColumn{C: schema.NewIntColumn("c2", "int")}) 173 changes, err := p.FixChange( 174 drv, 175 "ALTER TABLE t1 RENAME TO t2, ADD COLUMN c2 int", 176 schema.Changes{ 177 &schema.DropTable{T: schema.NewTable("t1").AddColumns(schema.NewIntColumn("c1", "int"))}, 178 &schema.AddTable{T: schema.NewTable("t2").AddColumns(schema.NewIntColumn("c1", "int"), schema.NewIntColumn("c2", "int"))}, 179 }, 180 ) 181 require.NoError(t, err) 182 require.Equal( 183 t, 184 schema.Changes{ 185 &schema.ModifyTable{ 186 T: schema.NewTable("t1").AddColumns(schema.NewIntColumn("c1", "int"), schema.NewIntColumn("c2", "int")), 187 Changes: schema.Changes{ 188 &schema.AddColumn{C: schema.NewIntColumn("c2", "int")}, 189 }, 190 }, 191 &schema.RenameTable{ 192 From: schema.NewTable("t1").AddColumns(schema.NewIntColumn("c1", "int"), schema.NewIntColumn("c2", "int")), 193 To: schema.NewTable("t2").AddColumns(schema.NewIntColumn("c1", "int"), schema.NewIntColumn("c2", "int")), 194 }, 195 }, 196 changes, 197 ) 198 } 199 200 func TestColumnFilledBefore(t *testing.T) { 201 for i, tt := range []struct { 202 file string 203 pos int 204 wantFilled bool 205 wantErr bool 206 }{ 207 { 208 file: `UPDATE t SET c = NULL;`, 209 pos: 100, 210 }, 211 { 212 file: `UPDATE t SET c = 2;`, 213 pos: 100, 214 wantFilled: true, 215 }, 216 { 217 file: `UPDATE t SET c = 2 WHERE c IS NULL;`, 218 pos: 100, 219 wantFilled: true, 220 }, 221 { 222 file: `UPDATE t SET c = 2 WHERE c IS NOT NULL;`, 223 pos: 100, 224 wantFilled: false, 225 }, 226 { 227 file: `UPDATE t SET c = 2 WHERE c <> NULL`, 228 pos: 100, 229 wantFilled: false, 230 }, 231 { 232 file: ` 233 ALTER TABLE t MODIFY COLUMN c INT NOT NULL; 234 UPDATE t SET c = 2 WHERE c IS NULL; 235 `, 236 pos: 2, 237 wantFilled: false, 238 }, 239 { 240 file: ` 241 UPDATE t SET c = 2 WHERE c IS NULL; 242 ALTER TABLE t MODIFY COLUMN c INT NOT NULL; 243 `, 244 pos: 30, 245 wantFilled: true, 246 }, 247 } { 248 t.Run(strconv.Itoa(i), func(t *testing.T) { 249 var ( 250 p myparse.Parser 251 f = migrate.NewLocalFile("file", []byte(tt.file)) 252 ) 253 filled, err := p.ColumnFilledBefore(f, schema.NewTable("t"), schema.NewColumn("c"), tt.pos) 254 require.Equal(t, err != nil, tt.wantErr, err) 255 require.Equal(t, filled, tt.wantFilled) 256 }) 257 } 258 } 259 260 func TestColumnFilledAfter(t *testing.T) { 261 for i, tt := range []struct { 262 file string 263 pos int 264 matchValue any 265 wantFilled bool 266 wantErr bool 267 }{ 268 { 269 file: ` 270 ALTER TABLE t MODIFY COLUMN c varchar(255) NOT NULL; 271 UPDATE t SET c = CONCAT('tenant_', d) WHERE c = ""; 272 `, 273 matchValue: `""`, 274 pos: 30, 275 wantFilled: true, 276 }, 277 { 278 file: ` 279 ALTER TABLE t MODIFY COLUMN c varchar(255) NOT NULL; 280 UPDATE t SET c = CONCAT('tenant_', d) WHERE c = ""; 281 `, 282 matchValue: "", 283 pos: 30, 284 wantFilled: true, 285 }, 286 { 287 file: ` 288 ALTER TABLE t MODIFY COLUMN c varchar(255) NOT NULL; 289 UPDATE t SET c = CONCAT('tenant_', d) WHERE c = ''; 290 `, 291 matchValue: "", 292 pos: 30, 293 wantFilled: true, 294 }, 295 { 296 file: ` 297 ALTER TABLE t MODIFY COLUMN c varchar(255) NOT NULL; 298 UPDATE t SET c = CONCAT('tenant_', d) WHERE c = 1; 299 `, 300 matchValue: 1, 301 pos: 30, 302 wantFilled: true, 303 }, 304 { 305 file: ` 306 ALTER TABLE t MODIFY COLUMN c varchar(255) NOT NULL; 307 UPDATE t SET c = CONCAT('tenant_', d) WHERE c = 0; 308 `, 309 matchValue: "0", 310 pos: 30, 311 wantFilled: true, 312 }, 313 { 314 file: ` 315 ALTER TABLE t MODIFY COLUMN c varchar(255) NOT NULL; 316 UPDATE t SET c = CONCAT('tenant_', d) WHERE c = 0; 317 `, 318 matchValue: "0", 319 pos: 100, 320 wantFilled: false, 321 }, 322 } { 323 t.Run(strconv.Itoa(i), func(t *testing.T) { 324 var ( 325 p myparse.Parser 326 f = migrate.NewLocalFile("file", []byte(tt.file)) 327 ) 328 filled, err := p.ColumnFilledAfter(f, schema.NewTable("t"), schema.NewColumn("c"), tt.pos, tt.matchValue) 329 require.Equal(t, err != nil, tt.wantErr, err) 330 require.Equal(t, filled, tt.wantFilled) 331 }) 332 } 333 } 334 335 func TestCreateViewAfter(t *testing.T) { 336 for i, tt := range []struct { 337 file string 338 pos int 339 wantCreated bool 340 wantErr bool 341 }{ 342 { 343 file: ` 344 ALTER TABLE old RENAME TO new; 345 CREATE VIEW old AS SELECT * FROM new; 346 `, 347 pos: 1, 348 wantCreated: true, 349 }, 350 { 351 file: ` 352 ALTER TABLE old RENAME TO new; 353 CREATE VIEW old AS SELECT * FROM users; 354 `, 355 pos: 1, 356 }, 357 { 358 file: ` 359 ALTER TABLE old RENAME TO new; 360 CREATE VIEW old AS SELECT * FROM new JOIN new; 361 `, 362 pos: 1, 363 }, 364 { 365 file: ` 366 ALTER TABLE old RENAME TO new; 367 CREATE VIEW old AS SELECT * FROM new; 368 `, 369 pos: 100, 370 }, 371 { 372 file: ` 373 ALTER TABLE old RENAME TO new; 374 CREATE VIEW old AS SELECT a, b, c FROM new; 375 `, 376 wantCreated: true, 377 }, 378 } { 379 t.Run(strconv.Itoa(i), func(t *testing.T) { 380 var ( 381 p myparse.Parser 382 f = migrate.NewLocalFile("file", []byte(tt.file)) 383 ) 384 created, err := p.CreateViewAfter(f, "old", "new", tt.pos) 385 require.Equal(t, err != nil, tt.wantErr, err) 386 require.Equal(t, created, tt.wantCreated) 387 }) 388 } 389 } 390 391 func TestColumnHasReferences(t *testing.T) { 392 for i, tt := range []struct { 393 stmt string 394 column string 395 wantHas bool 396 wantErr bool 397 }{ 398 { 399 stmt: "CREATE TABLE t(c int REFERENCES t(c));", 400 column: "c", 401 wantHas: true, 402 }, 403 { 404 stmt: "CREATE TABLE t(c int REFERENCES t(c));", 405 column: "d", 406 }, 407 { 408 stmt: "CREATE TABLE t(c int REFERENCES t(c), d int);", 409 column: "d", 410 }, 411 { 412 stmt: "ALTER TABLE t ADD COLUMN c int REFERENCES t(c);", 413 column: "c", 414 wantHas: true, 415 }, 416 { 417 stmt: "ALTER TABLE t ADD COLUMN c int REFERENCES t(c);", 418 column: "d", 419 }, 420 { 421 stmt: "ALTER TABLE t ADD COLUMN c int REFERENCES t(c), ADD d int;", 422 column: "d", 423 }, 424 } { 425 t.Run(strconv.Itoa(i), func(t *testing.T) { 426 var p myparse.Parser 427 hasR, err := p.ColumnHasReferences(&migrate.Stmt{Text: tt.stmt}, schema.NewColumn(tt.column)) 428 require.Equal(t, err != nil, tt.wantErr, err) 429 require.Equal(t, hasR, tt.wantHas) 430 }) 431 } 432 } 433 434 type mockDriver struct { 435 migrate.Driver 436 changes schema.Changes 437 } 438 439 func (d mockDriver) TableDiff(_, _ *schema.Table, _ ...schema.DiffOption) ([]schema.Change, error) { 440 return d.changes, nil 441 }