vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/controller_plan_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 vreplication
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  type testControllerPlan struct {
    27  	query             string
    28  	opcode            int
    29  	numInserts        int
    30  	selector          string
    31  	applier           string
    32  	delCopyState      string
    33  	delPostCopyAction string
    34  }
    35  
    36  func TestControllerPlan(t *testing.T) {
    37  	tcases := []struct {
    38  		in   string
    39  		plan *testControllerPlan
    40  		err  string
    41  	}{{
    42  		// Insert
    43  		in: "insert into _vt.vreplication values(null)",
    44  		plan: &testControllerPlan{
    45  			query:      "insert into _vt.vreplication values(null)",
    46  			opcode:     insertQuery,
    47  			numInserts: 1,
    48  		},
    49  	}, {
    50  		in: "insert into _vt.vreplication(id) values(null)",
    51  		plan: &testControllerPlan{
    52  			query:      "insert into _vt.vreplication(id) values(null)",
    53  			opcode:     insertQuery,
    54  			numInserts: 1,
    55  		},
    56  	}, {
    57  		in: "insert into _vt.vreplication(workflow, id) values('', null)",
    58  		plan: &testControllerPlan{
    59  			query:      "insert into _vt.vreplication(workflow, id) values('', null)",
    60  			opcode:     insertQuery,
    61  			numInserts: 1,
    62  		},
    63  	}, {
    64  		in: "insert into _vt.vreplication values(null), (null)",
    65  		plan: &testControllerPlan{
    66  			query:      "insert into _vt.vreplication values(null), (null)",
    67  			opcode:     insertQuery,
    68  			numInserts: 2,
    69  		},
    70  	}, {
    71  		in: "insert into _vt.resharding_journal values (1)",
    72  		plan: &testControllerPlan{
    73  			query:  "insert into _vt.resharding_journal values (1)",
    74  			opcode: reshardingJournalQuery,
    75  		},
    76  	}, {
    77  		in:  "replace into _vt.vreplication values(null)",
    78  		err: "unsupported construct: replace into _vt.vreplication values (null)",
    79  	}, {
    80  		in:  "insert ignore into _vt.vreplication values(null)",
    81  		err: "unsupported construct: insert ignore into _vt.vreplication values (null)",
    82  	}, {
    83  		in:  "insert into other values(null)",
    84  		err: "invalid table name: other",
    85  	}, {
    86  		in:  "insert into _vt.vreplication partition(a) values(null)",
    87  		err: "unsupported construct: insert into _vt.vreplication partition (a) values (null)",
    88  	}, {
    89  		in:  "insert into _vt.vreplication values(null) on duplicate key update id=3",
    90  		err: "unsupported construct: insert into _vt.vreplication values (null) on duplicate key update id = 3",
    91  	}, {
    92  		in:  "insert into _vt.vreplication select * from a",
    93  		err: "unsupported construct: insert into _vt.vreplication select * from a",
    94  	}, {
    95  		in:  "insert into _vt.vreplication(a, b, id) values(null)",
    96  		err: "malformed statement: insert into _vt.vreplication(a, b, id) values (null)",
    97  	}, {
    98  		in:  "insert into _vt.vreplication(workflow, id) values('aa', 1)",
    99  		err: "id should not have a value: insert into _vt.vreplication(workflow, id) values ('aa', 1)",
   100  	}, {
   101  		in:  "insert into _vt.vreplication values(1)",
   102  		err: "id should not have a value: insert into _vt.vreplication values (1)",
   103  
   104  		// Update
   105  	}, {
   106  		in: "update _vt.vreplication set state='Running' where id = 1",
   107  		plan: &testControllerPlan{
   108  			query:    "update _vt.vreplication set state='Running' where id = 1",
   109  			opcode:   updateQuery,
   110  			selector: "select id from _vt.vreplication where id = 1",
   111  			applier:  "update _vt.vreplication set state = 'Running' where id in ::ids",
   112  		},
   113  	}, {
   114  		in: "update _vt.vreplication set state='Running'",
   115  		plan: &testControllerPlan{
   116  			query:    "update _vt.vreplication set state='Running'",
   117  			opcode:   updateQuery,
   118  			selector: "select id from _vt.vreplication",
   119  			applier:  "update _vt.vreplication set state = 'Running' where id in ::ids",
   120  		},
   121  	}, {
   122  		in: "update _vt.vreplication set state='Running' where a = 1",
   123  		plan: &testControllerPlan{
   124  			query:    "update _vt.vreplication set state='Running' where a = 1",
   125  			opcode:   updateQuery,
   126  			selector: "select id from _vt.vreplication where a = 1",
   127  			applier:  "update _vt.vreplication set state = 'Running' where id in ::ids",
   128  		},
   129  	}, {
   130  		in: "update _vt.resharding_journal set col = 1",
   131  		plan: &testControllerPlan{
   132  			query:  "update _vt.resharding_journal set col = 1",
   133  			opcode: reshardingJournalQuery,
   134  		},
   135  	}, {
   136  		in:  "update a set state='Running' where id = 1",
   137  		err: "invalid table name: a",
   138  	}, {
   139  		in:  "update _vt.vreplication set state='Running' where id = 1 order by id",
   140  		err: "unsupported construct: update _vt.vreplication set state = 'Running' where id = 1 order by id asc",
   141  	}, {
   142  		in:  "update _vt.vreplication set state='Running' where id = 1 limit 1",
   143  		err: "unsupported construct: update _vt.vreplication set state = 'Running' where id = 1 limit 1",
   144  	}, {
   145  		in:  "update _vt.vreplication set state='Running', id = 2 where id = 1",
   146  		err: "id cannot be changed: id = 2",
   147  
   148  		// Delete
   149  	}, {
   150  		in: "delete from _vt.vreplication where id = 1",
   151  		plan: &testControllerPlan{
   152  			query:             "delete from _vt.vreplication where id = 1",
   153  			opcode:            deleteQuery,
   154  			selector:          "select id from _vt.vreplication where id = 1",
   155  			applier:           "delete from _vt.vreplication where id in ::ids",
   156  			delCopyState:      "delete from _vt.copy_state where vrepl_id in ::ids",
   157  			delPostCopyAction: "delete from _vt.post_copy_action where vrepl_id in ::ids",
   158  		},
   159  	}, {
   160  		in: "delete from _vt.vreplication",
   161  		plan: &testControllerPlan{
   162  			query:             "delete from _vt.vreplication",
   163  			opcode:            deleteQuery,
   164  			selector:          "select id from _vt.vreplication",
   165  			applier:           "delete from _vt.vreplication where id in ::ids",
   166  			delCopyState:      "delete from _vt.copy_state where vrepl_id in ::ids",
   167  			delPostCopyAction: "delete from _vt.post_copy_action where vrepl_id in ::ids",
   168  		},
   169  	}, {
   170  		in: "delete from _vt.vreplication where a = 1",
   171  		plan: &testControllerPlan{
   172  			query:             "delete from _vt.vreplication where a = 1",
   173  			opcode:            deleteQuery,
   174  			selector:          "select id from _vt.vreplication where a = 1",
   175  			applier:           "delete from _vt.vreplication where id in ::ids",
   176  			delCopyState:      "delete from _vt.copy_state where vrepl_id in ::ids",
   177  			delPostCopyAction: "delete from _vt.post_copy_action where vrepl_id in ::ids",
   178  		},
   179  	}, {
   180  		in: "delete from _vt.resharding_journal where id = 1",
   181  		plan: &testControllerPlan{
   182  			query:  "delete from _vt.resharding_journal where id = 1",
   183  			opcode: reshardingJournalQuery,
   184  		},
   185  	}, {
   186  		in:  "delete from a where id = 1",
   187  		err: "invalid table name: a",
   188  	}, {
   189  		in:  "delete a, b from _vt.vreplication where id = 1",
   190  		err: "unsupported construct: delete a, b from _vt.vreplication where id = 1",
   191  	}, {
   192  		in:  "delete from _vt.vreplication where id = 1 order by id",
   193  		err: "unsupported construct: delete from _vt.vreplication where id = 1 order by id asc",
   194  	}, {
   195  		in:  "delete from _vt.vreplication where id = 1 limit 1",
   196  		err: "unsupported construct: delete from _vt.vreplication where id = 1 limit 1",
   197  	}, {
   198  		in:  "delete from _vt.vreplication partition (a) where id = 1 limit 1",
   199  		err: "unsupported construct: delete from _vt.vreplication partition (a) where id = 1 limit 1",
   200  
   201  		// Select
   202  	}, {
   203  		in: "select * from _vt.vreplication",
   204  		plan: &testControllerPlan{
   205  			opcode: selectQuery,
   206  			query:  "select * from _vt.vreplication",
   207  		},
   208  	}, {
   209  		in: "select * from _vt.resharding_journal",
   210  		plan: &testControllerPlan{
   211  			opcode: selectQuery,
   212  			query:  "select * from _vt.resharding_journal",
   213  		},
   214  	}, {
   215  		in: "select * from _vt.copy_state",
   216  		plan: &testControllerPlan{
   217  			opcode: selectQuery,
   218  			query:  "select * from _vt.copy_state",
   219  		},
   220  	}, {
   221  		in:  "select * from a",
   222  		err: "invalid table name: a",
   223  
   224  		// Parser
   225  	}, {
   226  		in:  "bad query",
   227  		err: "syntax error at position 4 near 'bad'",
   228  	}, {
   229  		in:  "set a = 1",
   230  		err: "unsupported construct: set @@a = 1",
   231  	}}
   232  	for _, tcase := range tcases {
   233  		t.Run(tcase.in, func(t *testing.T) {
   234  			pl, err := buildControllerPlan(tcase.in)
   235  			if tcase.err != "" {
   236  				require.EqualError(t, err, tcase.err)
   237  				return
   238  			}
   239  			require.NoError(t, err)
   240  
   241  			gotPlan := &testControllerPlan{
   242  				query:      pl.query,
   243  				opcode:     pl.opcode,
   244  				numInserts: pl.numInserts,
   245  				selector:   pl.selector,
   246  			}
   247  			if pl.applier != nil {
   248  				gotPlan.applier = pl.applier.Query
   249  			}
   250  			if pl.delCopyState != nil {
   251  				gotPlan.delCopyState = pl.delCopyState.Query
   252  			}
   253  			if pl.delPostCopyAction != nil {
   254  				gotPlan.delPostCopyAction = pl.delPostCopyAction.Query
   255  			}
   256  			if !reflect.DeepEqual(gotPlan, tcase.plan) {
   257  				t.Errorf("getPlan(%v):\n%+v, want\n%+v", tcase.in, gotPlan, tcase.plan)
   258  			}
   259  		})
   260  	}
   261  }