vitess.io/vitess@v0.16.2/go/mysql/mysql56_gtid_set_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 mysql
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  )
    28  
    29  func TestSortSIDList(t *testing.T) {
    30  	input := []SID{
    31  		{1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
    32  		{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16},
    33  		{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
    34  		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    35  		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    36  		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    37  	}
    38  	want := []SID{
    39  		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    40  		{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    41  		{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
    42  		{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16},
    43  		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
    44  		{1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
    45  	}
    46  	sortSIDs(input)
    47  	assert.True(t, reflect.DeepEqual(input, want), "got %#v, want %#v", input, want)
    48  }
    49  
    50  func TestParseMysql56GTIDSet(t *testing.T) {
    51  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
    52  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255}
    53  
    54  	table := map[string]Mysql56GTIDSet{
    55  		// Empty
    56  		"": {},
    57  		// Simple case
    58  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5": {
    59  			sid1: []interval{{1, 5}},
    60  		},
    61  		// Capital hex chars
    62  		"00010203-0405-0607-0809-0A0B0C0D0E0F:1-5": {
    63  			sid1: []interval{{1, 5}},
    64  		},
    65  		// Interval with same start and end
    66  		"00010203-0405-0607-0809-0a0b0c0d0e0f:12": {
    67  			sid1: []interval{{12, 12}},
    68  		},
    69  		// Multiple intervals
    70  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20": {
    71  			sid1: []interval{{1, 5}, {10, 20}},
    72  		},
    73  		// Multiple intervals, out of order
    74  		"00010203-0405-0607-0809-0a0b0c0d0e0f:10-20:1-5": {
    75  			sid1: []interval{{1, 5}, {10, 20}},
    76  		},
    77  		// Intervals with end < start are discarded by MySQL 5.6
    78  		"00010203-0405-0607-0809-0a0b0c0d0e0f:8-7": {},
    79  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:8-7:10-20": {
    80  			sid1: []interval{{1, 5}, {10, 20}},
    81  		},
    82  		// Same repeating SIDs
    83  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5,00010203-0405-0607-0809-0a0b0c0d0e0f:10-20": {
    84  			sid1: []interval{{1, 5}, {10, 20}},
    85  		},
    86  		// Same repeating SIDs, backwards order
    87  		"00010203-0405-0607-0809-0a0b0c0d0e0f:10-20,00010203-0405-0607-0809-0a0b0c0d0e0f:1-5": {
    88  			sid1: []interval{{1, 5}, {10, 20}},
    89  		},
    90  		// Multiple SIDs
    91  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20,00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50": {
    92  			sid1: []interval{{1, 5}, {10, 20}},
    93  			sid2: []interval{{1, 5}, {50, 50}},
    94  		},
    95  		// Multiple SIDs with space around the comma
    96  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20, 00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50": {
    97  			sid1: []interval{{1, 5}, {10, 20}},
    98  			sid2: []interval{{1, 5}, {50, 50}},
    99  		},
   100  	}
   101  
   102  	for input, want := range table {
   103  		t.Run(input, func(t *testing.T) {
   104  			got, err := ParseMysql56GTIDSet(input)
   105  			require.NoError(t, err)
   106  			assert.Equal(t, want, got)
   107  		})
   108  	}
   109  }
   110  
   111  func TestParseMysql56GTIDSetInvalid(t *testing.T) {
   112  	table := []string{
   113  		// No intervals
   114  		"00010203-0405-0607-0809-0a0b0c0d0e0f",
   115  		// Invalid SID
   116  		"00010203-0405-060X-0809-0a0b0c0d0e0f:1-5",
   117  		// Invalid intervals
   118  		"00010203-0405-0607-0809-0a0b0c0d0e0f:0-5",
   119  		"00010203-0405-0607-0809-0a0b0c0d0e0f:-5",
   120  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-2-3",
   121  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-",
   122  	}
   123  
   124  	for _, input := range table {
   125  		_, err := ParseMysql56GTIDSet(input)
   126  		assert.Error(t, err, "parseMysql56GTIDSet(%#v) expected error, got none", err)
   127  	}
   128  }
   129  
   130  func TestMysql56GTIDSetString(t *testing.T) {
   131  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   132  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255}
   133  
   134  	table := map[string]Mysql56GTIDSet{
   135  		// Simple case
   136  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5": {
   137  			sid1: []interval{{1, 5}},
   138  		},
   139  		// Interval with same start and end
   140  		"00010203-0405-0607-0809-0a0b0c0d0e0f:12": {
   141  			sid1: []interval{{12, 12}},
   142  		},
   143  		// Multiple intervals
   144  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20": {
   145  			sid1: []interval{{1, 5}, {10, 20}},
   146  		},
   147  		// Multiple SIDs
   148  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20,00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50": {
   149  			sid1: []interval{{1, 5}, {10, 20}},
   150  			sid2: []interval{{1, 5}, {50, 50}},
   151  		},
   152  	}
   153  
   154  	for want, input := range table {
   155  		got := strings.ToLower(input.String())
   156  		assert.Equal(t, want, got, "%#v.String() = %#v, want %#v", input, got, want)
   157  
   158  	}
   159  }
   160  
   161  func TestMysql56GTIDSetFlavor(t *testing.T) {
   162  	input := Mysql56GTIDSet{}
   163  	if got, want := input.Flavor(), "MySQL56"; got != want {
   164  		t.Errorf("%#v.Flavor() = %#v, want %#v", input, got, want)
   165  	}
   166  }
   167  
   168  func TestMysql56GTIDSetContainsGTID(t *testing.T) {
   169  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   170  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
   171  	sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
   172  
   173  	set := Mysql56GTIDSet{
   174  		sid1: []interval{{20, 30}, {35, 40}},
   175  		sid2: []interval{{1, 5}, {50, 50}},
   176  	}
   177  
   178  	table := map[GTID]bool{
   179  		fakeGTID{}: false,
   180  
   181  		Mysql56GTID{sid1, 1}:  false,
   182  		Mysql56GTID{sid1, 19}: false,
   183  		Mysql56GTID{sid1, 20}: true,
   184  		Mysql56GTID{sid1, 23}: true,
   185  		Mysql56GTID{sid1, 30}: true,
   186  		Mysql56GTID{sid1, 31}: false,
   187  
   188  		Mysql56GTID{sid2, 1}:  true,
   189  		Mysql56GTID{sid2, 10}: false,
   190  		Mysql56GTID{sid2, 50}: true,
   191  		Mysql56GTID{sid2, 51}: false,
   192  
   193  		Mysql56GTID{sid3, 1}: false,
   194  	}
   195  
   196  	for input, want := range table {
   197  		if got := set.ContainsGTID(input); got != want {
   198  			t.Errorf("ContainsGTID(%#v) = %#v, want %#v", input, got, want)
   199  		}
   200  	}
   201  }
   202  
   203  func TestMysql56GTIDSetContains(t *testing.T) {
   204  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   205  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
   206  	sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
   207  
   208  	// The set to test against.
   209  	set := Mysql56GTIDSet{
   210  		sid1: []interval{{20, 30}, {35, 40}},
   211  		sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   212  	}
   213  
   214  	// Test cases that should return Contains() = true.
   215  	contained := []Mysql56GTIDSet{
   216  		// The set should contain itself.
   217  		set,
   218  		// Every set contains the empty set.
   219  		{},
   220  
   221  		// Simple case
   222  		{sid1: []interval{{25, 30}}},
   223  		// Multiple intervals
   224  		{sid2: []interval{{1, 2}, {4, 5}, {60, 70}}},
   225  		// Multiple SIDs
   226  		{
   227  			sid1: []interval{{25, 30}, {35, 37}},
   228  			sid2: []interval{{1, 5}},
   229  		},
   230  	}
   231  
   232  	for _, other := range contained {
   233  		assert.True(t, set.Contains(other), "Contains(%#v) = false, want true", other)
   234  
   235  	}
   236  
   237  	// Test cases that should return Contains() = false.
   238  	notContained := []GTIDSet{
   239  		// Wrong flavor is not contained.
   240  		fakeGTID{},
   241  
   242  		// Simple cases
   243  		Mysql56GTIDSet{sid1: []interval{{1, 5}}},
   244  		Mysql56GTIDSet{sid1: []interval{{10, 19}}},
   245  		// Overlapping intervals
   246  		Mysql56GTIDSet{sid1: []interval{{10, 20}}},
   247  		Mysql56GTIDSet{sid1: []interval{{10, 25}}},
   248  		Mysql56GTIDSet{sid1: []interval{{25, 31}}},
   249  		Mysql56GTIDSet{sid1: []interval{{30, 31}}},
   250  		// Multiple intervals
   251  		Mysql56GTIDSet{sid1: []interval{{20, 30}, {34, 34}}},
   252  		// Multiple SIDs
   253  		Mysql56GTIDSet{
   254  			sid1: []interval{{20, 30}, {36, 36}},
   255  			sid2: []interval{{3, 5}, {55, 60}},
   256  		},
   257  		// SID is missing entirely
   258  		Mysql56GTIDSet{sid3: []interval{{1, 5}}},
   259  	}
   260  
   261  	for _, other := range notContained {
   262  		if set.Contains(other) {
   263  			t.Errorf("Contains(%#v) = true, want false", other)
   264  		}
   265  	}
   266  }
   267  
   268  func TestMysql56GTIDSetContains2(t *testing.T) {
   269  	set1, err := ParseMysql56GTIDSet("16b1039f-22b6-11ed-b765-0a43f95f28a3:1-243")
   270  	require.NoError(t, err)
   271  	set2, err := ParseMysql56GTIDSet("16b1039f-22b6-11ed-b765-0a43f95f28a3:1-615")
   272  	require.NoError(t, err)
   273  	set3, err := ParseMysql56GTIDSet("16b1039f-22b6-11ed-b765-0a43f95f28a3:1-632")
   274  	require.NoError(t, err)
   275  	set4, err := ParseMysql56GTIDSet("16b1039f-22b6-11ed-b765-0a43f95f28a3:20-664")
   276  	require.NoError(t, err)
   277  	set5, err := ParseMysql56GTIDSet("16b1039f-22b6-11ed-b765-0a43f95f28a3:20-243")
   278  	require.NoError(t, err)
   279  
   280  	compareSet, err := ParseMysql56GTIDSet("16b1039f-22b6-11ed-b765-0a43f95f28a3:1-615")
   281  	require.NoError(t, err)
   282  
   283  	assert.True(t, compareSet.Contains(set1))
   284  	assert.True(t, compareSet.Contains(set2))
   285  	assert.False(t, compareSet.Contains(set3))
   286  	assert.False(t, compareSet.Contains(set4))
   287  	assert.True(t, compareSet.Contains(set5))
   288  }
   289  
   290  func TestMysql56GTIDSetEqual(t *testing.T) {
   291  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   292  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
   293  	sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
   294  
   295  	// The set to test against.
   296  	set := Mysql56GTIDSet{
   297  		sid1: []interval{{20, 30}, {35, 40}},
   298  		sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   299  	}
   300  
   301  	// Test cases that should return Equal() = true.
   302  	equal := []Mysql56GTIDSet{
   303  		// Same underlying map instance
   304  		set,
   305  		// Different instance, same data
   306  		{
   307  			sid1: []interval{{20, 30}, {35, 40}},
   308  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   309  		},
   310  	}
   311  
   312  	for _, other := range equal {
   313  		assert.True(t, set.Equal(other), "%#v.Equal(%#v) = false, want true", set, other)
   314  		// Equality should be transitive.
   315  		assert.True(t, other.Equal(set), "%#v.Equal(%#v) = false, want true", other, set)
   316  
   317  	}
   318  
   319  	// Test cases that should return Equal() = false.
   320  	notEqual := []GTIDSet{
   321  		// Wrong flavor is not equal.
   322  		fakeGTID{},
   323  		// Empty set
   324  		Mysql56GTIDSet{},
   325  		// Interval changed
   326  		Mysql56GTIDSet{
   327  			sid1: []interval{{20, 31}, {35, 40}},
   328  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   329  		},
   330  		// Interval added
   331  		Mysql56GTIDSet{
   332  			sid1: []interval{{20, 30}, {32, 33}, {35, 40}},
   333  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   334  		},
   335  		// Interval removed
   336  		Mysql56GTIDSet{
   337  			sid1: []interval{{20, 30}, {35, 40}},
   338  			sid2: []interval{{1, 5}, {60, 70}},
   339  		},
   340  		// Different SID, same intervals
   341  		Mysql56GTIDSet{
   342  			sid1: []interval{{20, 30}, {35, 40}},
   343  			sid3: []interval{{1, 5}, {50, 50}, {60, 70}},
   344  		},
   345  		// SID added
   346  		Mysql56GTIDSet{
   347  			sid1: []interval{{20, 30}, {35, 40}},
   348  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   349  			sid3: []interval{{1, 5}},
   350  		},
   351  		// SID removed
   352  		Mysql56GTIDSet{
   353  			sid1: []interval{{20, 30}, {35, 40}},
   354  		},
   355  	}
   356  
   357  	for _, other := range notEqual {
   358  		if set.Equal(other) {
   359  			t.Errorf("%#v.Equal(%#v) = true, want false", set, other)
   360  		}
   361  		// Equality should be transitive.
   362  		if other.Equal(set) {
   363  			t.Errorf("%#v.Equal(%#v) = true, want false", other, set)
   364  		}
   365  	}
   366  }
   367  
   368  func TestMysql56GTIDSetAddGTID(t *testing.T) {
   369  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   370  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
   371  	sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
   372  
   373  	// The set to test against.
   374  	set := Mysql56GTIDSet{
   375  		sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   376  		sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   377  	}
   378  
   379  	table := map[GTID]Mysql56GTIDSet{
   380  		// Adding wrong flavor is a no-op.
   381  		fakeGTID{}: set,
   382  
   383  		// Adding GTIDs that are already in the set
   384  		Mysql56GTID{Server: sid1, Sequence: 20}: {
   385  			sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   386  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   387  		},
   388  		Mysql56GTID{Server: sid1, Sequence: 30}: {
   389  			sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   390  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   391  		},
   392  		Mysql56GTID{Server: sid1, Sequence: 25}: {
   393  			sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   394  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   395  		},
   396  		// New interval beginning
   397  		Mysql56GTID{Server: sid1, Sequence: 1}: {
   398  			sid1: []interval{{1, 1}, {20, 30}, {35, 40}, {42, 45}},
   399  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   400  		},
   401  		// New interval middle
   402  		Mysql56GTID{Server: sid1, Sequence: 32}: {
   403  			sid1: []interval{{20, 30}, {32, 32}, {35, 40}, {42, 45}},
   404  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   405  		},
   406  		// New interval end
   407  		Mysql56GTID{Server: sid1, Sequence: 50}: {
   408  			sid1: []interval{{20, 30}, {35, 40}, {42, 45}, {50, 50}},
   409  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   410  		},
   411  		// Extend interval start
   412  		Mysql56GTID{Server: sid2, Sequence: 49}: {
   413  			sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   414  			sid2: []interval{{1, 5}, {49, 50}, {60, 70}},
   415  		},
   416  		// Extend interval end
   417  		Mysql56GTID{Server: sid2, Sequence: 51}: {
   418  			sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   419  			sid2: []interval{{1, 5}, {50, 51}, {60, 70}},
   420  		},
   421  		// Merge intervals
   422  		Mysql56GTID{Server: sid1, Sequence: 41}: {
   423  			sid1: []interval{{20, 30}, {35, 45}},
   424  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   425  		},
   426  		// Different SID
   427  		Mysql56GTID{Server: sid3, Sequence: 1}: {
   428  			sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   429  			sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
   430  			sid3: []interval{{1, 1}},
   431  		},
   432  	}
   433  
   434  	for input, want := range table {
   435  		if got := set.AddGTID(input); !got.Equal(want) {
   436  			t.Errorf("AddGTID(%#v) = %#v, want %#v", input, got, want)
   437  		}
   438  	}
   439  }
   440  
   441  func TestMysql56GTIDSetUnion(t *testing.T) {
   442  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   443  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
   444  	sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
   445  
   446  	set1 := Mysql56GTIDSet{
   447  		sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
   448  		sid2: []interval{{1, 5}, {20, 50}, {60, 70}},
   449  	}
   450  
   451  	set2 := Mysql56GTIDSet{
   452  		sid1: []interval{{20, 31}, {35, 37}, {41, 46}},
   453  		sid2: []interval{{3, 6}, {22, 49}, {67, 72}},
   454  		sid3: []interval{{1, 45}},
   455  	}
   456  
   457  	got := set1.Union(set2)
   458  
   459  	want := Mysql56GTIDSet{
   460  		sid1: []interval{{20, 31}, {35, 46}},
   461  		sid2: []interval{{1, 6}, {20, 50}, {60, 72}},
   462  		sid3: []interval{{1, 45}},
   463  	}
   464  	assert.True(t, got.Equal(want), "set1: %#v, set1.Union(%#v) = %#v, want %#v", set1, set2, got, want)
   465  
   466  }
   467  
   468  func TestMysql56GTIDSetDifference(t *testing.T) {
   469  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   470  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
   471  	sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
   472  	sid4 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 18}
   473  	sid5 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 19}
   474  
   475  	set1 := Mysql56GTIDSet{
   476  		sid1: []interval{{20, 30}, {35, 39}, {40, 53}, {55, 75}},
   477  		sid2: []interval{{1, 7}, {20, 50}, {60, 70}},
   478  		sid4: []interval{{1, 30}},
   479  		sid5: []interval{{1, 7}, {20, 30}},
   480  	}
   481  
   482  	set2 := Mysql56GTIDSet{
   483  		sid1: []interval{{20, 30}, {35, 37}, {50, 60}},
   484  		sid2: []interval{{3, 5}, {22, 25}, {32, 37}, {67, 70}},
   485  		sid3: []interval{{1, 45}},
   486  		sid5: []interval{{2, 6}, {15, 40}},
   487  	}
   488  
   489  	got := set1.Difference(set2)
   490  
   491  	want := Mysql56GTIDSet{
   492  		sid1: []interval{{38, 39}, {40, 49}, {61, 75}},
   493  		sid2: []interval{{1, 2}, {6, 7}, {20, 21}, {26, 31}, {38, 50}, {60, 66}},
   494  		sid4: []interval{{1, 30}},
   495  		sid5: []interval{{1, 1}, {7, 7}},
   496  	}
   497  	assert.True(t, got.Equal(want), "got %#v; want %#v", got, want)
   498  
   499  	sid10 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   500  	sid11 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   501  	set10 := Mysql56GTIDSet{
   502  		sid10: []interval{{1, 30}},
   503  	}
   504  	set11 := Mysql56GTIDSet{
   505  		sid11: []interval{{1, 30}},
   506  	}
   507  	got = set10.Difference(set11)
   508  	want = Mysql56GTIDSet{}
   509  	assert.True(t, got.Equal(want), "got %#v; want %#v", got, want)
   510  
   511  }
   512  
   513  func TestMysql56GTIDSetSIDBlock(t *testing.T) {
   514  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   515  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
   516  
   517  	input := Mysql56GTIDSet{
   518  		sid1: []interval{{20, 30}, {35, 40}},
   519  		sid2: []interval{{1, 5}},
   520  	}
   521  	want := []byte{
   522  		// n_sids
   523  		2, 0, 0, 0, 0, 0, 0, 0,
   524  		// sid1
   525  		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
   526  		// sid1: n_intervals
   527  		2, 0, 0, 0, 0, 0, 0, 0,
   528  		// sid1: interval 1 start
   529  		20, 0, 0, 0, 0, 0, 0, 0,
   530  		// sid1: interval 1 end
   531  		31, 0, 0, 0, 0, 0, 0, 0,
   532  		// sid1: interval 2 start
   533  		35, 0, 0, 0, 0, 0, 0, 0,
   534  		// sid1: interval 2 end
   535  		41, 0, 0, 0, 0, 0, 0, 0,
   536  		// sid2
   537  		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16,
   538  		// sid2: n_intervals
   539  		1, 0, 0, 0, 0, 0, 0, 0,
   540  		// sid2: interval 1 start
   541  		1, 0, 0, 0, 0, 0, 0, 0,
   542  		// sid2: interval 1 end
   543  		6, 0, 0, 0, 0, 0, 0, 0,
   544  	}
   545  	got := input.SIDBlock()
   546  	assert.True(t, reflect.DeepEqual(got, want), "%#v.SIDBlock() = %#v, want %#v", input, got, want)
   547  
   548  	// Testing the conversion back.
   549  	set, err := NewMysql56GTIDSetFromSIDBlock(want)
   550  	require.NoError(t, err, "Reconstructing Mysql56GTIDSet from SID block failed: %v", err)
   551  	assert.True(t, reflect.DeepEqual(set, input), "NewMysql56GTIDSetFromSIDBlock(%#v) = %#v, want %#v", want, set, input)
   552  
   553  }
   554  
   555  func TestMySQL56GTIDSetLast(t *testing.T) {
   556  	sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
   557  	sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255}
   558  
   559  	table := map[string]Mysql56GTIDSet{
   560  		// Simple case
   561  		"00010203-0405-0607-0809-0a0b0c0d0e0f:5": {
   562  			sid1: []interval{{1, 5}},
   563  		},
   564  		"00010203-0405-0607-0809-0a0b0c0d0e0f:3": {
   565  			sid1: []interval{{end: 3}},
   566  		},
   567  		// Interval with same start and end
   568  		"00010203-0405-0607-0809-0a0b0c0d0e0f:12": {
   569  			sid1: []interval{{12, 12}},
   570  		},
   571  		// Multiple intervals
   572  		"00010203-0405-0607-0809-0a0b0c0d0e0f:20": {
   573  			sid1: []interval{{1, 5}, {10, 20}},
   574  		},
   575  		// Multiple SIDs
   576  		"00010203-0405-0607-0809-0a0b0c0d0eff:50": {
   577  			sid1: []interval{{1, 5}, {10, 20}},
   578  			sid2: []interval{{1, 5}, {50, 50}},
   579  		},
   580  	}
   581  
   582  	for want, input := range table {
   583  		got := strings.ToLower(input.Last())
   584  		assert.Equal(t, want, got)
   585  	}
   586  }
   587  
   588  func TestSubtract(t *testing.T) {
   589  	tests := []struct {
   590  		name       string
   591  		lhs        string
   592  		rhs        string
   593  		difference string
   594  		wantErr    string
   595  	}{
   596  		{
   597  			name:       "Extra GTID set on left side",
   598  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65cca-3fe4-11ed-bbfb-091034d48b3e:1",
   599  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   600  			difference: "8bc65cca-3fe4-11ed-bbfb-091034d48b3e:1",
   601  		}, {
   602  			name:       "Extra GTID set on right side",
   603  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   604  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65cca-3fe4-11ed-bbfb-091034d48b3e:1",
   605  			difference: "",
   606  		}, {
   607  			name:       "Empty left side",
   608  			lhs:        "",
   609  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   610  			difference: "",
   611  		}, {
   612  			name:       "Empty right side",
   613  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65cca-3fe4-11ed-bbfb-091034d48b3e:1",
   614  			rhs:        "",
   615  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65cca-3fe4-11ed-bbfb-091034d48b3e:1",
   616  		}, {
   617  			name:       "Equal sets",
   618  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65cca-3fe4-11ed-bbfb-091034d48b3e:1",
   619  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65cca-3fe4-11ed-bbfb-091034d48b3e:1",
   620  			difference: "",
   621  		}, {
   622  			name:       "subtract prefix",
   623  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   624  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-3",
   625  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:4-8",
   626  		}, {
   627  			name:       "subtract mid",
   628  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   629  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:2-3",
   630  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1:4-8",
   631  		}, {
   632  			name:       "subtract suffix",
   633  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   634  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:7-8",
   635  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-6",
   636  		}, {
   637  			name:       "subtract complex range 1",
   638  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8:12-17",
   639  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:7-8",
   640  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-6:12-17",
   641  		}, {
   642  			name:       "subtract complex range 2",
   643  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8:12-17",
   644  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:12-13",
   645  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8:14-17",
   646  		}, {
   647  			name:       "subtract complex range 3",
   648  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8:12-17",
   649  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:7-13",
   650  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-6:14-17",
   651  		}, {
   652  			name:       "subtract repeating uuid",
   653  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:12-17",
   654  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:7-13",
   655  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-6:14-17",
   656  		}, {
   657  			name:       "subtract repeating uuid in descending order",
   658  			lhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:12-17,8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   659  			rhs:        "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:7-13",
   660  			difference: "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-6:14-17",
   661  		}, {
   662  			name:    "parsing error in left set",
   663  			lhs:     "incorrect set",
   664  			rhs:     "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   665  			wantErr: `invalid MySQL 5.6 GTID set ("incorrect set"): expected uuid:interval`,
   666  		}, {
   667  			name:    "parsing error in right set",
   668  			lhs:     "8bc65c84-3fe4-11ed-a912-257f0fcdd6c9:1-8",
   669  			rhs:     "incorrect set",
   670  			wantErr: `invalid MySQL 5.6 GTID set ("incorrect set"): expected uuid:interval`,
   671  		},
   672  	}
   673  	for _, tt := range tests {
   674  		t.Run(fmt.Sprintf("%s: %s-%s", tt.name, tt.lhs, tt.rhs), func(t *testing.T) {
   675  			got, err := Subtract(tt.lhs, tt.rhs)
   676  			if tt.wantErr != "" {
   677  				assert.EqualError(t, err, tt.wantErr)
   678  			} else {
   679  				assert.NoError(t, err)
   680  				assert.Equal(t, tt.difference, got)
   681  			}
   682  		})
   683  	}
   684  }
   685  
   686  func BenchmarkMySQL56GTIDParsing(b *testing.B) {
   687  	var Inputs = []string{
   688  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5",
   689  		"00010203-0405-0607-0809-0a0b0c0d0e0f:12",
   690  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20",
   691  		"00010203-0405-0607-0809-0a0b0c0d0e0f:10-20:1-5",
   692  		"00010203-0405-0607-0809-0a0b0c0d0e0f:8-7",
   693  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:8-7:10-20",
   694  		"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20,00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50",
   695  		"8aabbf4f-5074-11ed-b225-aa23ce7e3ba2:1-20443,a6f1bf40-5073-11ed-9c0f-12a3889dc912:1-343402",
   696  	}
   697  
   698  	b.ReportAllocs()
   699  	b.ResetTimer()
   700  
   701  	for n := 0; n < b.N; n++ {
   702  		for _, input := range Inputs {
   703  			_, _ = ParseMysql56GTIDSet(input)
   704  		}
   705  	}
   706  }