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 }