vitess.io/vitess@v0.16.2/go/vt/schemadiff/view_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  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"vitess.io/vitess/go/vt/sqlparser"
    26  )
    27  
    28  func TestCreateViewDiff(t *testing.T) {
    29  	tt := []struct {
    30  		name     string
    31  		from     string
    32  		to       string
    33  		fromName string
    34  		toName   string
    35  		diff     string
    36  		cdiff    string
    37  		isError  bool
    38  	}{
    39  		{
    40  			name: "identical",
    41  			from: "create view v1 as select a, b, c from t",
    42  			to:   "create view v1 as select a, b, c from t",
    43  		},
    44  		{
    45  			name: "identical, case change",
    46  			from: "create view v1 as SELECT a, b, c from t",
    47  			to:   "create view v1 as select a, b, c from t",
    48  		},
    49  		{
    50  			name: "identical, case change on target",
    51  			from: "create view v1 as select a, b, c from t",
    52  			to:   "create view v1 as SELECT a, b, c from t",
    53  		},
    54  		{
    55  			name: "identical, case and qualifiers",
    56  			from: "create view v1 as select `a`, `b`, c from t",
    57  			to:   "create view v1 as SELECT a, b, `c` from t",
    58  		},
    59  		{
    60  			name: "identical, column list, qualified",
    61  			from: "create view v1 (col1, `col2`, `col3`) as select `a`, `b`, c from t",
    62  			to:   "create view v1 (`col1`, col2, col3) as select a, b, `c` from t",
    63  		},
    64  		{
    65  			name:     "change of column list, qualifiers",
    66  			from:     "create view v1 (col1, `col2`, `col3`) as select `a`, `b`, c from t",
    67  			to:       "create view v1 (`col1`, col2, colother) as select a, b, `c` from t",
    68  			diff:     "alter view v1(col1, col2, colother) as select a, b, c from t",
    69  			cdiff:    "ALTER VIEW `v1`(`col1`, `col2`, `colother`) AS SELECT `a`, `b`, `c` FROM `t`",
    70  			fromName: "v1",
    71  			toName:   "v1",
    72  		},
    73  		{
    74  			name:  "change of column list, must have qualifiers",
    75  			from:  "create view v1 (col1, `col2`, `col3`) as select `a`, `b`, c from t",
    76  			to:    "create view v1 (`col1.with.dot`, `col2`, colother) as select a, b, `c` from t",
    77  			diff:  "alter view v1(`col1.with.dot`, col2, colother) as select a, b, c from t",
    78  			cdiff: "ALTER VIEW `v1`(`col1.with.dot`, `col2`, `colother`) AS SELECT `a`, `b`, `c` FROM `t`",
    79  		},
    80  		{
    81  			name: "identical, spacing, case change",
    82  			from: "create view v1 as select a, b, c FROM    t",
    83  			to: `create view v1 as
    84  				SELECT a, b, c
    85  				from t`,
    86  		},
    87  		{
    88  			name:  "change of query",
    89  			from:  "create view v1 as select a from t",
    90  			to:    "create view v1 as select a, b from t",
    91  			diff:  "alter view v1 as select a, b from t",
    92  			cdiff: "ALTER VIEW `v1` AS SELECT `a`, `b` FROM `t`",
    93  		},
    94  		{
    95  			name:  "change of view name",
    96  			from:  "create view v1 as select a from t",
    97  			to:    "create view v2 as select a, b from t",
    98  			diff:  "alter view v1 as select a, b from t",
    99  			cdiff: "ALTER VIEW `v1` AS SELECT `a`, `b` FROM `t`",
   100  		},
   101  		{
   102  			name: "change of columns, spacing",
   103  			from: "create view v1 as select a from t",
   104  			to: `create view v2 as
   105  				select a, b
   106  				from t`,
   107  			diff:     "alter view v1 as select a, b from t",
   108  			cdiff:    "ALTER VIEW `v1` AS SELECT `a`, `b` FROM `t`",
   109  			fromName: "v1",
   110  			toName:   "v2",
   111  		},
   112  		{
   113  			name:  "algorithm, case change",
   114  			from:  "create view v1 as select a from t",
   115  			to:    "create algorithm=temptable view v2 as select a FROM t",
   116  			diff:  "alter algorithm = temptable view v1 as select a from t",
   117  			cdiff: "ALTER ALGORITHM = TEMPTABLE VIEW `v1` AS SELECT `a` FROM `t`",
   118  		},
   119  		{
   120  			name:  "algorithm, case change 2",
   121  			from:  "create view v1 as select a FROM t",
   122  			to:    "create algorithm=temptable view v2 as select a from t",
   123  			diff:  "alter algorithm = temptable view v1 as select a from t",
   124  			cdiff: "ALTER ALGORITHM = TEMPTABLE VIEW `v1` AS SELECT `a` FROM `t`",
   125  		},
   126  		{
   127  			name:  "algorithm, case change 3",
   128  			from:  "create ALGORITHM=MERGE view v1 as select a FROM t",
   129  			to:    "create ALGORITHM=TEMPTABLE view v2 as select a from t",
   130  			diff:  "alter algorithm = TEMPTABLE view v1 as select a from t",
   131  			cdiff: "ALTER ALGORITHM = TEMPTABLE VIEW `v1` AS SELECT `a` FROM `t`",
   132  		},
   133  		{
   134  			name:  "algorithm value is case sensitive",
   135  			from:  "create ALGORITHM=TEMPTABLE view v1 as select a from t",
   136  			to:    "create ALGORITHM=temptable view v2 as select a from t",
   137  			diff:  "alter algorithm = temptable view v1 as select a from t",
   138  			cdiff: "ALTER ALGORITHM = TEMPTABLE VIEW `v1` AS SELECT `a` FROM `t`",
   139  		},
   140  		{
   141  			name:  "algorithm value is case sensitive 2",
   142  			from:  "create ALGORITHM=temptable view v1 as select a from t",
   143  			to:    "create ALGORITHM=TEMPTABLE view v2 as select a from t",
   144  			diff:  "alter algorithm = TEMPTABLE view v1 as select a from t",
   145  			cdiff: "ALTER ALGORITHM = TEMPTABLE VIEW `v1` AS SELECT `a` FROM `t`",
   146  		},
   147  	}
   148  	hints := &DiffHints{}
   149  	for _, ts := range tt {
   150  		t.Run(ts.name, func(t *testing.T) {
   151  			fromStmt, err := sqlparser.ParseStrictDDL(ts.from)
   152  			assert.NoError(t, err)
   153  			fromCreateView, ok := fromStmt.(*sqlparser.CreateView)
   154  			assert.True(t, ok)
   155  
   156  			toStmt, err := sqlparser.ParseStrictDDL(ts.to)
   157  			assert.NoError(t, err)
   158  			toCreateView, ok := toStmt.(*sqlparser.CreateView)
   159  			assert.True(t, ok)
   160  
   161  			c, err := NewCreateViewEntity(fromCreateView)
   162  			require.NoError(t, err)
   163  			other, err := NewCreateViewEntity(toCreateView)
   164  			require.NoError(t, err)
   165  			alter, err := c.Diff(other, hints)
   166  			switch {
   167  			case ts.isError:
   168  				assert.Error(t, err)
   169  			case ts.diff == "":
   170  				assert.NoError(t, err)
   171  				assert.Nil(t, alter)
   172  			default:
   173  				assert.NoError(t, err)
   174  				require.NotNil(t, alter)
   175  				require.False(t, alter.IsEmpty())
   176  				{
   177  					diff := alter.StatementString()
   178  					assert.Equal(t, ts.diff, diff)
   179  					// validate we can parse back the statement
   180  					_, err := sqlparser.ParseStrictDDL(diff)
   181  					assert.NoError(t, err)
   182  
   183  					eFrom, eTo := alter.Entities()
   184  					if ts.fromName != "" {
   185  						assert.Equal(t, ts.fromName, eFrom.Name())
   186  					}
   187  					if ts.toName != "" {
   188  						assert.Equal(t, ts.toName, eTo.Name())
   189  					}
   190  					{ // Validate "apply()" on "from" converges with "to"
   191  						applied, err := c.Apply(alter)
   192  						assert.NoError(t, err)
   193  						require.NotNil(t, applied)
   194  						appliedDiff, err := eTo.Diff(applied, hints)
   195  						require.NoError(t, err)
   196  						assert.True(t, appliedDiff.IsEmpty(), "expected empty diff, found changes: %v", appliedDiff.CanonicalStatementString())
   197  					}
   198  				}
   199  				{
   200  					cdiff := alter.CanonicalStatementString()
   201  					assert.Equal(t, ts.cdiff, cdiff)
   202  					_, err := sqlparser.ParseStrictDDL(cdiff)
   203  					assert.NoError(t, err)
   204  				}
   205  			}
   206  		})
   207  	}
   208  }
   209  
   210  func TestNormalizeView(t *testing.T) {
   211  	tt := []struct {
   212  		name string
   213  		from string
   214  		to   string
   215  	}{
   216  		{
   217  			name: "basic view",
   218  			from: "create view v1 as select a, b, c from t",
   219  			to:   "CREATE VIEW `v1` AS SELECT `a`, `b`, `c` FROM `t`",
   220  		},
   221  		{
   222  			name: "default algorithm",
   223  			from: "create algorithm=undefined view v1 as select a, b, c from t",
   224  			to:   "CREATE VIEW `v1` AS SELECT `a`, `b`, `c` FROM `t`",
   225  		},
   226  		{
   227  			name: "non-default algorithm",
   228  			from: "create algorithm=merge view v1 as select a, b, c from t",
   229  			to:   "CREATE ALGORITHM = merge VIEW `v1` AS SELECT `a`, `b`, `c` FROM `t`",
   230  		},
   231  		{
   232  			name: "default security model",
   233  			from: "create sql security DEFINER view v1 as select a, b, c from t",
   234  			to:   "CREATE VIEW `v1` AS SELECT `a`, `b`, `c` FROM `t`",
   235  		},
   236  		{
   237  			name: "non-default security model",
   238  			from: "create sql security invoker view v1 as select a, b, c from t",
   239  			to:   "CREATE SQL SECURITY INVOKER VIEW `v1` AS SELECT `a`, `b`, `c` FROM `t`",
   240  		},
   241  	}
   242  	for _, ts := range tt {
   243  		t.Run(ts.name, func(t *testing.T) {
   244  			stmt, err := sqlparser.ParseStrictDDL(ts.from)
   245  			require.NoError(t, err)
   246  			fromCreateView, ok := stmt.(*sqlparser.CreateView)
   247  			require.True(t, ok)
   248  
   249  			from, err := NewCreateViewEntity(fromCreateView)
   250  			require.NoError(t, err)
   251  			assert.Equal(t, ts.to, sqlparser.CanonicalString(from))
   252  		})
   253  	}
   254  }