vitess.io/vitess@v0.16.2/go/mysql/mariadb_gtid.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  	"sort"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"vitess.io/vitess/go/vt/proto/vtrpc"
    26  	"vitess.io/vitess/go/vt/vterrors"
    27  )
    28  
    29  // MariadbFlavorID is the string identifier for the MariaDB flavor.
    30  const MariadbFlavorID = "MariaDB"
    31  
    32  // parseMariadbGTID is registered as a GTID parser.
    33  func parseMariadbGTID(s string) (GTID, error) {
    34  	// Split into parts.
    35  	parts := strings.Split(s, "-")
    36  	if len(parts) != 3 {
    37  		return nil, vterrors.Errorf(vtrpc.Code_INTERNAL, "invalid MariaDB GTID (%v): expecting Domain-Server-Sequence", s)
    38  	}
    39  
    40  	// Parse Domain ID.
    41  	Domain, err := strconv.ParseUint(parts[0], 10, 32)
    42  	if err != nil {
    43  		return nil, vterrors.Wrapf(err, "invalid MariaDB GTID Domain ID (%v)", parts[0])
    44  	}
    45  
    46  	// Parse Server ID.
    47  	Server, err := strconv.ParseUint(parts[1], 10, 32)
    48  	if err != nil {
    49  		return nil, vterrors.Wrapf(err, "invalid MariaDB GTID Server ID (%v)", parts[1])
    50  	}
    51  
    52  	// Parse Sequence number.
    53  	Sequence, err := strconv.ParseUint(parts[2], 10, 64)
    54  	if err != nil {
    55  		return nil, vterrors.Wrapf(err, "invalid MariaDB GTID Sequence number (%v)", parts[2])
    56  	}
    57  
    58  	return MariadbGTID{
    59  		Domain:   uint32(Domain),
    60  		Server:   uint32(Server),
    61  		Sequence: Sequence,
    62  	}, nil
    63  }
    64  
    65  // parseMariadbGTIDSet is registered as a GTIDSet parser.
    66  func parseMariadbGTIDSet(s string) (GTIDSet, error) {
    67  	gtidStrings := strings.Split(s, ",")
    68  	gtidSet := make(MariadbGTIDSet, len(gtidStrings))
    69  	for _, gtidString := range gtidStrings {
    70  		gtidString = strings.TrimSpace(gtidString)
    71  		if gtidString == "" {
    72  			continue
    73  		}
    74  		gtid, err := parseMariadbGTID(gtidString)
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  		mdbGTID := gtid.(MariadbGTID)
    79  		gtidSet[mdbGTID.Domain] = mdbGTID
    80  	}
    81  	return gtidSet, nil
    82  }
    83  
    84  // MariadbGTID implements GTID.
    85  type MariadbGTID struct {
    86  	// Domain is the ID number of the domain within which sequence numbers apply.
    87  	Domain uint32
    88  	// Server is the ID of the server that generated the transaction.
    89  	Server uint32
    90  	// Sequence is the sequence number of the transaction within the domain.
    91  	Sequence uint64
    92  }
    93  
    94  // String implements GTID.String().
    95  func (gtid MariadbGTID) String() string {
    96  	return fmt.Sprintf("%d-%d-%d", gtid.Domain, gtid.Server, gtid.Sequence)
    97  }
    98  
    99  // Flavor implements GTID.Flavor().
   100  func (gtid MariadbGTID) Flavor() string {
   101  	return MariadbFlavorID
   102  }
   103  
   104  // SequenceDomain implements GTID.SequenceDomain().
   105  func (gtid MariadbGTID) SequenceDomain() any {
   106  	return gtid.Domain
   107  }
   108  
   109  // SourceServer implements GTID.SourceServer().
   110  func (gtid MariadbGTID) SourceServer() any {
   111  	return gtid.Server
   112  }
   113  
   114  // SequenceNumber implements GTID.SequenceNumber().
   115  func (gtid MariadbGTID) SequenceNumber() any {
   116  	return gtid.Sequence
   117  }
   118  
   119  // GTIDSet implements GTID.GTIDSet().
   120  func (gtid MariadbGTID) GTIDSet() GTIDSet {
   121  	return MariadbGTIDSet{gtid.Domain: gtid}
   122  }
   123  
   124  // MariadbGTIDSet implements GTIDSet.
   125  type MariadbGTIDSet map[uint32]MariadbGTID
   126  
   127  // String implements GTIDSet.String()
   128  func (gtidSet MariadbGTIDSet) String() string {
   129  	// Sort domains so the string format is deterministic.
   130  	domains := make([]uint32, 0, len(gtidSet))
   131  	for domain := range gtidSet {
   132  		domains = append(domains, domain)
   133  	}
   134  	sort.Slice(domains, func(i, j int) bool {
   135  		return domains[i] < domains[j]
   136  	})
   137  
   138  	// Convert each domain's GTID to a string and join all with comma.
   139  	s := make([]string, len(gtidSet))
   140  	for i, domain := range domains {
   141  		s[i] = gtidSet[domain].String()
   142  	}
   143  	return strings.Join(s, ",")
   144  }
   145  
   146  // Flavor implements GTIDSet.Flavor()
   147  func (gtidSet MariadbGTIDSet) Flavor() string {
   148  	return MariadbFlavorID
   149  }
   150  
   151  // ContainsGTID implements GTIDSet.ContainsGTID().
   152  func (gtidSet MariadbGTIDSet) ContainsGTID(other GTID) bool {
   153  	if other == nil {
   154  		return true
   155  	}
   156  	mdbOther, ok := other.(MariadbGTID)
   157  	if !ok {
   158  		return false
   159  	}
   160  	gtid, ok := gtidSet[mdbOther.Domain]
   161  	if !ok {
   162  		return false
   163  	}
   164  	return gtid.Sequence >= mdbOther.Sequence
   165  }
   166  
   167  // Contains implements GTIDSet.Contains().
   168  func (gtidSet MariadbGTIDSet) Contains(other GTIDSet) bool {
   169  	if other == nil {
   170  		return false
   171  	}
   172  	mdbOther, ok := other.(MariadbGTIDSet)
   173  	if !ok {
   174  		return false
   175  	}
   176  	for _, gtid := range mdbOther {
   177  		if !gtidSet.ContainsGTID(gtid) {
   178  			return false
   179  		}
   180  	}
   181  	return true
   182  }
   183  
   184  // Equal implements GTIDSet.Equal().
   185  func (gtidSet MariadbGTIDSet) Equal(other GTIDSet) bool {
   186  	mdbOther, ok := other.(MariadbGTIDSet)
   187  	if !ok {
   188  		return false
   189  	}
   190  	if len(gtidSet) != len(mdbOther) {
   191  		return false
   192  	}
   193  	for domain, gtid := range gtidSet {
   194  		otherGTID, ok := mdbOther[domain]
   195  		if !ok {
   196  			return false
   197  		}
   198  		if gtid != otherGTID {
   199  			return false
   200  		}
   201  	}
   202  	return true
   203  }
   204  
   205  // AddGTID implements GTIDSet.AddGTID().
   206  func (gtidSet MariadbGTIDSet) AddGTID(other GTID) GTIDSet {
   207  	if other == nil {
   208  		return gtidSet
   209  	}
   210  	mdbOther, ok := other.(MariadbGTID)
   211  	if !ok {
   212  		return gtidSet
   213  	}
   214  	newSet := gtidSet.deepCopy()
   215  	newSet.addGTID(mdbOther)
   216  	return newSet
   217  }
   218  
   219  // Union implements GTIDSet.Union(). This is a pure method, and does not mutate the receiver.
   220  func (gtidSet MariadbGTIDSet) Union(other GTIDSet) GTIDSet {
   221  	if gtidSet == nil && other != nil {
   222  		return other
   223  	}
   224  	if gtidSet == nil || other == nil {
   225  		return gtidSet
   226  	}
   227  
   228  	mdbOther, ok := other.(MariadbGTIDSet)
   229  	if !ok {
   230  		return gtidSet
   231  	}
   232  
   233  	newSet := gtidSet.deepCopy()
   234  	for _, otherGTID := range mdbOther {
   235  		newSet.addGTID(otherGTID)
   236  	}
   237  	return newSet
   238  }
   239  
   240  // Last returns the last gtid
   241  func (gtidSet MariadbGTIDSet) Last() string {
   242  	// Sort domains so the string format is deterministic.
   243  	domains := make([]uint32, 0, len(gtidSet))
   244  	for domain := range gtidSet {
   245  		domains = append(domains, domain)
   246  	}
   247  	sort.Slice(domains, func(i, j int) bool {
   248  		return domains[i] < domains[j]
   249  	})
   250  
   251  	lastGTID := domains[len(gtidSet)-1]
   252  	return gtidSet[lastGTID].String()
   253  }
   254  
   255  // deepCopy returns a deep copy of the set.
   256  func (gtidSet MariadbGTIDSet) deepCopy() MariadbGTIDSet {
   257  	newSet := make(MariadbGTIDSet, len(gtidSet))
   258  	for domain, gtid := range gtidSet {
   259  		newSet[domain] = gtid
   260  	}
   261  	return newSet
   262  }
   263  
   264  // addGTID is an internal method that adds a GTID to the set.
   265  // Unlike the exported methods, this mutates the receiver.
   266  func (gtidSet MariadbGTIDSet) addGTID(otherGTID MariadbGTID) {
   267  	gtid, ok := gtidSet[otherGTID.Domain]
   268  	if !ok || otherGTID.Sequence > gtid.Sequence {
   269  		gtidSet[otherGTID.Domain] = otherGTID
   270  	}
   271  }
   272  
   273  func init() {
   274  	gtidParsers[MariadbFlavorID] = parseMariadbGTID
   275  	gtidSetParsers[MariadbFlavorID] = parseMariadbGTIDSet
   276  }