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  }