зеркало из https://github.com/github/vitess-gh.git
Merge pull request #668 from youtube/mysql56
Implement GTID and GTIDSet for MySQL 5.6.
This commit is contained in:
Коммит
3b90724a36
|
@ -0,0 +1,6 @@
|
|||
# Options for enabling GTID
|
||||
# https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-howto.html
|
||||
gtid_mode = ON
|
||||
log_bin
|
||||
log_slave_updates
|
||||
enforce_gtid_consistency
|
Двоичные данные
data/bootstrap/mysql-db-dir_5.6.24.tbz
Двоичные данные
data/bootstrap/mysql-db-dir_5.6.24.tbz
Двоичный файл не отображается.
|
@ -31,7 +31,8 @@ mysqlctl $mysqlctl_args shutdown
|
|||
newfile=mysql-db-dir_$(cat $tablet_dir/data/mysql_upgrade_info).tbz
|
||||
|
||||
echo Creating new bootstrap file: $newfile
|
||||
(cd $tablet_dir && tar -jcf data.tbz data innodb)
|
||||
# Remove auto-generated UUID file, if present (MySQL 5.6).
|
||||
(cd $tablet_dir && rm -f data/auto.cnf && tar -jcf data.tbz data innodb)
|
||||
mv $tablet_dir/data.tbz ./$newfile
|
||||
|
||||
echo Removing tablet directory
|
||||
|
|
|
@ -32,7 +32,18 @@ dbconfig_flags="\
|
|||
# Set up environment.
|
||||
export LD_LIBRARY_PATH=$VTROOT/dist/vt-zookeeper-3.3.5/lib:$LD_LIBRARY_PATH
|
||||
export ZK_CLIENT_CONFIG=$script_root/zk-client-conf.json
|
||||
export EXTRA_MY_CNF=$VTROOT/config/mycnf/master_mariadb.cnf
|
||||
|
||||
case "$MYSQL_FLAVOR" in
|
||||
"MySQL56")
|
||||
bootstrap_archive=mysql-db-dir_5.6.24.tbz
|
||||
export EXTRA_MY_CNF=$VTROOT/config/mycnf/master_mysql56.cnf
|
||||
;;
|
||||
"MariaDB")
|
||||
bootstrap_archive=mysql-db-dir_10.0.13-MariaDB.tbz
|
||||
export EXTRA_MY_CNF=$VTROOT/config/mycnf/master_mariadb.cnf
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p $VTDATAROOT/tmp
|
||||
|
||||
# Try to find mysqld_safe on PATH.
|
||||
|
@ -63,7 +74,7 @@ for uid_index in 0 1 2; do
|
|||
echo "Starting MySQL for tablet $alias..."
|
||||
$VTROOT/bin/mysqlctl -log_dir $VTDATAROOT/tmp -tablet_uid $uid $dbconfig_flags \
|
||||
-mysql_port $mysql_port \
|
||||
init -bootstrap_archive mysql-db-dir_10.0.13-MariaDB.tbz
|
||||
init -bootstrap_archive $bootstrap_archive
|
||||
|
||||
$VT_MYSQL_ROOT/bin/mysql -u vt_dba -S $VTDATAROOT/$tablet_dir/mysql.sock \
|
||||
-e "CREATE DATABASE IF NOT EXISTS vt_$keyspace"
|
||||
|
|
|
@ -21,8 +21,13 @@ import (
|
|||
var (
|
||||
binlogStreamerErrors = stats.NewCounters("BinlogStreamerErrors")
|
||||
|
||||
ClientEOF = fmt.Errorf("binlog stream consumer ended the reply stream")
|
||||
ServerEOF = fmt.Errorf("binlog stream connection was closed by mysqld")
|
||||
// ErrClientEOF is returned by BinlogStreamer if the stream ended because the
|
||||
// consumer of the stream indicated it doesn't want any more events.
|
||||
ErrClientEOF = fmt.Errorf("binlog stream consumer ended the reply stream")
|
||||
// ErrServerEOF is returned by BinlogStreamer if the stream ended because the
|
||||
// connection to the mysqld server was lost, or the stream was terminated by
|
||||
// mysqld.
|
||||
ErrServerEOF = fmt.Errorf("binlog stream connection was closed by mysqld")
|
||||
|
||||
// statementPrefixes are normal sql statement prefixes.
|
||||
statementPrefixes = map[string]int{
|
||||
|
@ -132,8 +137,8 @@ func (bls *BinlogStreamer) Stream(ctx *sync2.ServiceContext) (err error) {
|
|||
// at a time, and groups them into transactions. It is called from within the
|
||||
// service function launched by Stream().
|
||||
//
|
||||
// If the sendTransaction func returns io.EOF, parseEvents returns ClientEOF.
|
||||
// If the events channel is closed, parseEvents returns ServerEOF.
|
||||
// If the sendTransaction func returns io.EOF, parseEvents returns ErrClientEOF.
|
||||
// If the events channel is closed, parseEvents returns ErrServerEOF.
|
||||
func (bls *BinlogStreamer) parseEvents(ctx *sync2.ServiceContext, events <-chan proto.BinlogEvent) (myproto.ReplicationPosition, error) {
|
||||
var statements []proto.Statement
|
||||
var format proto.BinlogFormat
|
||||
|
@ -162,7 +167,7 @@ func (bls *BinlogStreamer) parseEvents(ctx *sync2.ServiceContext, events <-chan
|
|||
}
|
||||
if err = bls.sendTransaction(trans); err != nil {
|
||||
if err == io.EOF {
|
||||
return ClientEOF
|
||||
return ErrClientEOF
|
||||
}
|
||||
return fmt.Errorf("send reply error: %v", err)
|
||||
}
|
||||
|
@ -181,7 +186,7 @@ func (bls *BinlogStreamer) parseEvents(ctx *sync2.ServiceContext, events <-chan
|
|||
if !ok {
|
||||
// events channel has been closed, which means the connection died.
|
||||
log.Infof("reached end of binlog event stream")
|
||||
return pos, ServerEOF
|
||||
return pos, ErrServerEOF
|
||||
}
|
||||
case <-ctx.ShuttingDown:
|
||||
log.Infof("stopping early due to BinlogStreamer service shutdown")
|
||||
|
@ -217,7 +222,10 @@ func (bls *BinlogStreamer) parseEvents(ctx *sync2.ServiceContext, events <-chan
|
|||
}
|
||||
|
||||
// Strip the checksum, if any. We don't actually verify the checksum, so discard it.
|
||||
ev, _ = ev.StripChecksum(format)
|
||||
ev, _, err = ev.StripChecksum(format)
|
||||
if err != nil {
|
||||
return pos, fmt.Errorf("can't strip checksum from binlog event: %v, event data: %#v", err, ev)
|
||||
}
|
||||
|
||||
// Update the GTID if the event has one. The actual event type could be
|
||||
// something special like GTID_EVENT (MariaDB, MySQL 5.6), or it could be
|
||||
|
|
|
@ -49,17 +49,23 @@ func (fakeEvent) IntVar(proto.BinlogFormat) (string, uint64, error) {
|
|||
func (fakeEvent) Rand(proto.BinlogFormat) (uint64, uint64, error) {
|
||||
return 0, 0, errors.New("not a rand")
|
||||
}
|
||||
func (ev fakeEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) { return ev, nil }
|
||||
func (ev fakeEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type invalidEvent struct{ fakeEvent }
|
||||
|
||||
func (invalidEvent) IsValid() bool { return false }
|
||||
func (ev invalidEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) { return ev, nil }
|
||||
func (invalidEvent) IsValid() bool { return false }
|
||||
func (ev invalidEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type rotateEvent struct{ fakeEvent }
|
||||
|
||||
func (rotateEvent) IsRotate() bool { return true }
|
||||
func (ev rotateEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) { return ev, nil }
|
||||
func (rotateEvent) IsRotate() bool { return true }
|
||||
func (ev rotateEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type formatEvent struct{ fakeEvent }
|
||||
|
||||
|
@ -67,15 +73,17 @@ func (formatEvent) IsFormatDescription() bool { return true }
|
|||
func (formatEvent) Format() (proto.BinlogFormat, error) {
|
||||
return proto.BinlogFormat{FormatVersion: 1}, nil
|
||||
}
|
||||
func (ev formatEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) { return ev, nil }
|
||||
func (ev formatEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type invalidFormatEvent struct{ formatEvent }
|
||||
|
||||
func (invalidFormatEvent) Format() (proto.BinlogFormat, error) {
|
||||
return proto.BinlogFormat{}, errors.New("invalid format event")
|
||||
}
|
||||
func (ev invalidFormatEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) {
|
||||
return ev, nil
|
||||
func (ev invalidFormatEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type queryEvent struct {
|
||||
|
@ -87,21 +95,25 @@ func (queryEvent) IsQuery() bool { return true }
|
|||
func (ev queryEvent) Query(proto.BinlogFormat) (proto.Query, error) {
|
||||
return ev.query, nil
|
||||
}
|
||||
func (ev queryEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) { return ev, nil }
|
||||
func (ev queryEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type invalidQueryEvent struct{ queryEvent }
|
||||
|
||||
func (invalidQueryEvent) Query(proto.BinlogFormat) (proto.Query, error) {
|
||||
return proto.Query{}, errors.New("invalid query event")
|
||||
}
|
||||
func (ev invalidQueryEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) {
|
||||
return ev, nil
|
||||
func (ev invalidQueryEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type xidEvent struct{ fakeEvent }
|
||||
|
||||
func (xidEvent) IsXID() bool { return true }
|
||||
func (ev xidEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) { return ev, nil }
|
||||
func (xidEvent) IsXID() bool { return true }
|
||||
func (ev xidEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type intVarEvent struct {
|
||||
fakeEvent
|
||||
|
@ -113,15 +125,17 @@ func (intVarEvent) IsIntVar() bool { return true }
|
|||
func (ev intVarEvent) IntVar(proto.BinlogFormat) (string, uint64, error) {
|
||||
return ev.name, ev.value, nil
|
||||
}
|
||||
func (ev intVarEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) { return ev, nil }
|
||||
func (ev intVarEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
type invalidIntVarEvent struct{ intVarEvent }
|
||||
|
||||
func (invalidIntVarEvent) IntVar(proto.BinlogFormat) (string, uint64, error) {
|
||||
return "", 0, errors.New("invalid intvar event")
|
||||
}
|
||||
func (ev invalidIntVarEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte) {
|
||||
return ev, nil
|
||||
func (ev invalidIntVarEvent) StripChecksum(proto.BinlogFormat) (proto.BinlogEvent, []byte, error) {
|
||||
return ev, nil, nil
|
||||
}
|
||||
|
||||
// sample MariaDB event data
|
||||
|
@ -179,7 +193,7 @@ func TestBinlogStreamerParseEventsXID(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -223,7 +237,7 @@ func TestBinlogStreamerParseEventsCommit(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -271,7 +285,7 @@ func TestBinlogStreamerParseEventsClientEOF(t *testing.T) {
|
|||
queryEvent{query: proto.Query{Database: "vt_test_keyspace", Sql: []byte("insert into vt_a(eid, id) values (1, 1) /* _stream vt_a (eid id ) (1 1 ); */")}},
|
||||
xidEvent{},
|
||||
}
|
||||
want := ClientEOF
|
||||
want := ErrClientEOF
|
||||
|
||||
events := make(chan proto.BinlogEvent)
|
||||
|
||||
|
@ -296,7 +310,7 @@ func TestBinlogStreamerParseEventsClientEOF(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBinlogStreamerParseEventsServerEOF(t *testing.T) {
|
||||
want := ServerEOF
|
||||
want := ErrServerEOF
|
||||
|
||||
events := make(chan proto.BinlogEvent)
|
||||
close(events)
|
||||
|
@ -563,7 +577,7 @@ func TestBinlogStreamerParseEventsRollback(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -612,7 +626,7 @@ func TestBinlogStreamerParseEventsDMLWithoutBegin(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -662,7 +676,7 @@ func TestBinlogStreamerParseEventsBeginWithoutCommit(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -708,7 +722,7 @@ func TestBinlogStreamerParseEventsSetInsertID(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -787,7 +801,7 @@ func TestBinlogStreamerParseEventsOtherDB(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -832,7 +846,7 @@ func TestBinlogStreamerParseEventsOtherDBBegin(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -864,7 +878,7 @@ func TestBinlogStreamerParseEventsBeginAgain(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
after := binlogStreamerErrors.Counts()["ParseEvents"]
|
||||
|
@ -908,7 +922,7 @@ func TestBinlogStreamerParseEventsMariadbBeginGTID(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
@ -951,7 +965,7 @@ func TestBinlogStreamerParseEventsMariadbStandaloneGTID(t *testing.T) {
|
|||
_, err := bls.parseEvents(ctx, events)
|
||||
return err
|
||||
})
|
||||
if err := svm.Join(); err != ServerEOF {
|
||||
if err := svm.Join(); err != ErrServerEOF {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ type BinlogEvent interface {
|
|||
// StripChecksum returns the checksum and a modified event with the checksum
|
||||
// stripped off, if any. If there is no checksum, it returns the same event
|
||||
// and a nil checksum.
|
||||
StripChecksum(BinlogFormat) (ev BinlogEvent, checksum []byte)
|
||||
StripChecksum(BinlogFormat) (ev BinlogEvent, checksum []byte, err error)
|
||||
}
|
||||
|
||||
// BinlogFormat contains relevant data from the FORMAT_DESCRIPTION_EVENT.
|
||||
|
|
|
@ -264,3 +264,13 @@ func (ev binlogEvent) Rand(f blproto.BinlogFormat) (seed1 uint64, seed2 uint64,
|
|||
func (ev binlogEvent) IsBeginGTID(f blproto.BinlogFormat) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// These constants are common between MariaDB 10.0 and MySQL 5.6.
|
||||
const (
|
||||
// BinlogChecksumAlgOff indicates that checksums are supported but off.
|
||||
BinlogChecksumAlgOff = 0
|
||||
// BinlogChecksumAlgCRC32 indicates that CRC32 checksums are used.
|
||||
BinlogChecksumAlgCRC32 = 1
|
||||
// BinlogChecksumAlgUndef indicates that checksums are not supported.
|
||||
BinlogChecksumAlgUndef = 255
|
||||
)
|
||||
|
|
|
@ -264,28 +264,21 @@ func (ev mariadbBinlogEvent) Format() (f blproto.BinlogFormat, err error) {
|
|||
}
|
||||
|
||||
// StripChecksum implements BinlogEvent.StripChecksum().
|
||||
func (ev mariadbBinlogEvent) StripChecksum(f blproto.BinlogFormat) (blproto.BinlogEvent, []byte) {
|
||||
func (ev mariadbBinlogEvent) StripChecksum(f blproto.BinlogFormat) (blproto.BinlogEvent, []byte, error) {
|
||||
switch f.ChecksumAlgorithm {
|
||||
case BinlogChecksumAlgOff, BinlogChecksumAlgUndef:
|
||||
// There is no checksum.
|
||||
return ev, nil
|
||||
return ev, nil, nil
|
||||
default:
|
||||
// Checksum is the last 4 bytes of the event buffer.
|
||||
data := ev.Bytes()
|
||||
length := len(data)
|
||||
checksum := data[length-4:]
|
||||
data = data[:length-4]
|
||||
return mariadbBinlogEvent{binlogEvent: binlogEvent(data)}, checksum
|
||||
return mariadbBinlogEvent{binlogEvent: binlogEvent(data)}, checksum, nil
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// BinlogChecksumAlgOff indicates that checksums are supported but off.
|
||||
BinlogChecksumAlgOff = 0
|
||||
// BinlogChecksumAlgUndef indicates that checksums are not supported.
|
||||
BinlogChecksumAlgUndef = 255
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerFlavorBuiltin(mariadbFlavorID, &mariaDB10{})
|
||||
}
|
||||
|
|
|
@ -139,14 +139,16 @@ func TestMariadbBinlogEventChecksumFormat(t *testing.T) {
|
|||
func TestMariadbBinlogEventStripChecksum(t *testing.T) {
|
||||
f, err := (mariadbBinlogEvent{binlogEvent: binlogEvent(mariadbChecksumFormatEvent)}).Format()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
input := mariadbBinlogEvent{binlogEvent: binlogEvent(mariadbChecksumQueryEvent)}
|
||||
wantEvent := mariadbBinlogEvent{binlogEvent: binlogEvent(mariadbChecksumStrippedQueryEvent)}
|
||||
wantChecksum := []byte{0xce, 0x49, 0x7a, 0x53}
|
||||
gotEvent, gotChecksum := input.StripChecksum(f)
|
||||
gotEvent, gotChecksum, err := input.StripChecksum(f)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(gotEvent, wantEvent) || !reflect.DeepEqual(gotChecksum, wantChecksum) {
|
||||
t.Errorf("%#v.StripChecksum() = (%v, %v), want (%v, %v)", input, gotEvent, gotChecksum, wantEvent, wantChecksum)
|
||||
}
|
||||
|
@ -155,13 +157,15 @@ func TestMariadbBinlogEventStripChecksum(t *testing.T) {
|
|||
func TestMariadbBinlogEventStripChecksumNone(t *testing.T) {
|
||||
f, err := (mariadbBinlogEvent{binlogEvent: binlogEvent(mariadbFormatEvent)}).Format()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
input := mariadbBinlogEvent{binlogEvent: binlogEvent(mariadbStandaloneGTIDEvent)}
|
||||
want := input
|
||||
gotEvent, gotChecksum := input.StripChecksum(f)
|
||||
gotEvent, gotChecksum, err := input.StripChecksum(f)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(gotEvent, want) || gotChecksum != nil {
|
||||
t.Errorf("%#v.StripChecksum() = (%v, %v), want (%v, nil)", input, gotEvent, gotChecksum, want)
|
||||
}
|
||||
|
|
|
@ -30,16 +30,16 @@ type GTID interface {
|
|||
Flavor() string
|
||||
|
||||
// SourceServer returns the ID of the server that generated the transaction.
|
||||
SourceServer() string
|
||||
SourceServer() interface{}
|
||||
|
||||
// SequenceNumber returns the ID number that increases with each transaction.
|
||||
// It is only valid to compare the sequence numbers of two GTIDs if they have
|
||||
// the same domain value.
|
||||
SequenceNumber() uint64
|
||||
SequenceNumber() interface{}
|
||||
|
||||
// SequenceDomain returns the ID of the domain within which two sequence
|
||||
// numbers can be meaningfully compared.
|
||||
SequenceDomain() string
|
||||
SequenceDomain() interface{}
|
||||
|
||||
// GTIDSet returns a GTIDSet of the same flavor as this GTID, containing only
|
||||
// this GTID.
|
||||
|
|
|
@ -21,10 +21,7 @@ type GTIDSet interface {
|
|||
// registered in the transactionSetParsers map.
|
||||
Flavor() string
|
||||
|
||||
// Last returns the GTID of the most recent transaction in the set.
|
||||
Last() GTID
|
||||
|
||||
// Contains returns true if the set contains the specified transaction.
|
||||
// ContainsGTID returns true if the set contains the specified transaction.
|
||||
ContainsGTID(GTID) bool
|
||||
|
||||
// Contains returns true if the set is a superset of another set.
|
||||
|
|
|
@ -421,15 +421,20 @@ type fakeGTID struct {
|
|||
flavor, value string
|
||||
}
|
||||
|
||||
func (f fakeGTID) String() string { return f.value }
|
||||
func (f fakeGTID) Flavor() string { return f.flavor }
|
||||
func (fakeGTID) SourceServer() string { return "" }
|
||||
func (fakeGTID) SequenceNumber() uint64 { return 0 }
|
||||
func (fakeGTID) SequenceDomain() string { return "" }
|
||||
func (f fakeGTID) GTIDSet() GTIDSet { return nil }
|
||||
func (f fakeGTID) String() string { return f.value }
|
||||
func (f fakeGTID) Flavor() string { return f.flavor }
|
||||
func (fakeGTID) SourceServer() interface{} { return int(1) }
|
||||
func (fakeGTID) SequenceNumber() interface{} { return int(1) }
|
||||
func (fakeGTID) SequenceDomain() interface{} { return int(1) }
|
||||
func (f fakeGTID) GTIDSet() GTIDSet { return nil }
|
||||
|
||||
func (fakeGTID) Last() GTID { return nil }
|
||||
func (fakeGTID) ContainsGTID(GTID) bool { return false }
|
||||
func (fakeGTID) Contains(GTIDSet) bool { return false }
|
||||
func (f fakeGTID) Equal(other GTIDSet) bool { return f == other.(fakeGTID) }
|
||||
func (fakeGTID) AddGTID(GTID) GTIDSet { return nil }
|
||||
func (fakeGTID) ContainsGTID(GTID) bool { return false }
|
||||
func (fakeGTID) Contains(GTIDSet) bool { return false }
|
||||
func (f fakeGTID) Equal(other GTIDSet) bool {
|
||||
otherFake, ok := other.(fakeGTID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return f == otherFake
|
||||
}
|
||||
func (fakeGTID) AddGTID(GTID) GTIDSet { return nil }
|
||||
|
|
|
@ -75,17 +75,17 @@ func (gtid MariadbGTID) Flavor() string {
|
|||
}
|
||||
|
||||
// SequenceDomain implements GTID.SequenceDomain().
|
||||
func (gtid MariadbGTID) SequenceDomain() string {
|
||||
return strconv.FormatUint(uint64(gtid.Domain), 10)
|
||||
func (gtid MariadbGTID) SequenceDomain() interface{} {
|
||||
return gtid.Domain
|
||||
}
|
||||
|
||||
// SourceServer implements GTID.SourceServer().
|
||||
func (gtid MariadbGTID) SourceServer() string {
|
||||
return strconv.FormatUint(uint64(gtid.Server), 10)
|
||||
func (gtid MariadbGTID) SourceServer() interface{} {
|
||||
return gtid.Server
|
||||
}
|
||||
|
||||
// SequenceNumber implements GTID.SequenceNumber().
|
||||
func (gtid MariadbGTID) SequenceNumber() uint64 {
|
||||
func (gtid MariadbGTID) SequenceNumber() interface{} {
|
||||
return gtid.Sequence
|
||||
}
|
||||
|
||||
|
@ -94,11 +94,6 @@ func (gtid MariadbGTID) GTIDSet() GTIDSet {
|
|||
return gtid
|
||||
}
|
||||
|
||||
// Last implements GTIDSet.Last().
|
||||
func (gtid MariadbGTID) Last() GTID {
|
||||
return gtid
|
||||
}
|
||||
|
||||
// ContainsGTID implements GTIDSet.ContainsGTID().
|
||||
func (gtid MariadbGTID) ContainsGTID(other GTID) bool {
|
||||
if other == nil {
|
||||
|
|
|
@ -123,31 +123,31 @@ func TestMariaGTIDFlavor(t *testing.T) {
|
|||
|
||||
func TestMariaGTIDSequenceDomain(t *testing.T) {
|
||||
input := MariadbGTID{Domain: 12, Server: 345, Sequence: 6789}
|
||||
want := "12"
|
||||
want := interface{}(uint32(12))
|
||||
|
||||
got := input.SequenceDomain()
|
||||
if got != want {
|
||||
t.Errorf("%#v.SequenceDomain() = '%v', want '%v'", input, got, want)
|
||||
t.Errorf("%#v.SequenceDomain() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMariaGTIDSourceServer(t *testing.T) {
|
||||
input := MariadbGTID{Domain: 12, Server: 345, Sequence: 6789}
|
||||
want := "345"
|
||||
want := interface{}(uint32(345))
|
||||
|
||||
got := input.SourceServer()
|
||||
if got != want {
|
||||
t.Errorf("%#v.SourceServer() = '%v', want '%v'", input, got, want)
|
||||
t.Errorf("%#v.SourceServer() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMariaGTIDSequenceNumber(t *testing.T) {
|
||||
input := MariadbGTID{Domain: 12, Server: 345, Sequence: 6789}
|
||||
want := uint64(6789)
|
||||
want := interface{}(uint64(6789))
|
||||
|
||||
got := input.SequenceNumber()
|
||||
if got != want {
|
||||
t.Errorf("%#v.SequenceNumber() = %v, want %v", input, got, want)
|
||||
t.Errorf("%#v.SequenceNumber() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,16 +161,6 @@ func TestMariaGTIDGTIDSet(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMariaGTIDLast(t *testing.T) {
|
||||
input := MariadbGTID{Domain: 12, Server: 345, Sequence: 6789}
|
||||
want := GTID(input)
|
||||
|
||||
got := input.Last()
|
||||
if got != want {
|
||||
t.Errorf("%#v.Last() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMariaGTIDContainsLess(t *testing.T) {
|
||||
input1 := MariadbGTID{Domain: 5, Server: 4727, Sequence: 300}
|
||||
input2 := MariadbGTID{Domain: 5, Server: 4727, Sequence: 700}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const mysql56FlavorID = "MySQL56"
|
||||
|
||||
// parseMysql56GTID is registered as a GTID parser.
|
||||
func parseMysql56GTID(s string) (GTID, error) {
|
||||
// Split into parts.
|
||||
parts := strings.Split(s, ":")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid MySQL 5.6 GTID (%v): expecting UUID:Sequence", s)
|
||||
}
|
||||
|
||||
// Parse Server ID.
|
||||
sid, err := ParseSID(parts[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid MySQL 5.6 GTID Server ID (%v): %v", parts[0], err)
|
||||
}
|
||||
|
||||
// Parse Sequence number.
|
||||
seq, err := strconv.ParseInt(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid MySQL 5.6 GTID Sequence number (%v): %v", parts[1], err)
|
||||
}
|
||||
|
||||
return Mysql56GTID{Server: sid, Sequence: seq}, nil
|
||||
}
|
||||
|
||||
// SID is the 16-byte unique ID of a MySQL 5.6 server.
|
||||
type SID [16]byte
|
||||
|
||||
// String prints an SID in the form used by MySQL 5.6.
|
||||
func (sid SID) String() string {
|
||||
dst := []byte("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
|
||||
hex.Encode(dst, sid[:4])
|
||||
hex.Encode(dst[9:], sid[4:6])
|
||||
hex.Encode(dst[14:], sid[6:8])
|
||||
hex.Encode(dst[19:], sid[8:10])
|
||||
hex.Encode(dst[24:], sid[10:16])
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
// ParseSID parses an SID in the form used by MySQL 5.6.
|
||||
func ParseSID(s string) (sid SID, err error) {
|
||||
if len(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return sid, fmt.Errorf("invalid MySQL 5.6 SID %q", s)
|
||||
}
|
||||
|
||||
// Drop the dashes so we can just check the error of Decode once.
|
||||
b := make([]byte, 0, 32)
|
||||
b = append(b, s[:8]...)
|
||||
b = append(b, s[9:13]...)
|
||||
b = append(b, s[14:18]...)
|
||||
b = append(b, s[19:23]...)
|
||||
b = append(b, s[24:]...)
|
||||
|
||||
if _, err := hex.Decode(sid[:], b); err != nil {
|
||||
return sid, fmt.Errorf("invalid MySQL 5.6 SID %q: %v", s, err)
|
||||
}
|
||||
return sid, nil
|
||||
}
|
||||
|
||||
// Mysql56GTID implements GTID
|
||||
type Mysql56GTID struct {
|
||||
// Server is the SID of the server that originally committed the transaction.
|
||||
Server SID
|
||||
// Sequence is the sequence number of the transaction within a given Server's
|
||||
// scope.
|
||||
Sequence int64
|
||||
}
|
||||
|
||||
// String implements GTID.String().
|
||||
func (gtid Mysql56GTID) String() string {
|
||||
return fmt.Sprintf("%s:%d", gtid.Server, gtid.Sequence)
|
||||
}
|
||||
|
||||
// Flavor implements GTID.Flavor().
|
||||
func (gtid Mysql56GTID) Flavor() string {
|
||||
return mysql56FlavorID
|
||||
}
|
||||
|
||||
// SequenceDomain implements GTID.SequenceDomain().
|
||||
func (gtid Mysql56GTID) SequenceDomain() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SourceServer implements GTID.SourceServer().
|
||||
func (gtid Mysql56GTID) SourceServer() interface{} {
|
||||
return gtid.Server
|
||||
}
|
||||
|
||||
// SequenceNumber implements GTID.SequenceNumber().
|
||||
func (gtid Mysql56GTID) SequenceNumber() interface{} {
|
||||
return gtid.Sequence
|
||||
}
|
||||
|
||||
// GTIDSet implements GTID.GTIDSet().
|
||||
func (gtid Mysql56GTID) GTIDSet() GTIDSet {
|
||||
return Mysql56GTIDSet{}.AddGTID(gtid)
|
||||
}
|
||||
|
||||
func init() {
|
||||
gtidParsers[mysql56FlavorID] = parseMysql56GTID
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type interval struct {
|
||||
start, end int64
|
||||
}
|
||||
|
||||
func (iv interval) contains(other interval) bool {
|
||||
return iv.start <= other.start && other.end <= iv.end
|
||||
}
|
||||
|
||||
type intervalList []interval
|
||||
|
||||
// Len implements sort.Interface.
|
||||
func (s intervalList) Len() int { return len(s) }
|
||||
|
||||
// Less implements sort.Interface.
|
||||
func (s intervalList) Less(i, j int) bool { return s[i].start < s[j].start }
|
||||
|
||||
// Swap implements sort.Interface.
|
||||
func (s intervalList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
func parseInterval(s string) (interval, error) {
|
||||
parts := strings.Split(s, "-")
|
||||
start, err := strconv.ParseInt(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
return interval{}, fmt.Errorf("invalid interval (%q): %v", s, err)
|
||||
}
|
||||
if start < 1 {
|
||||
return interval{}, fmt.Errorf("invalid interval (%q): start must be > 0", s)
|
||||
}
|
||||
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
return interval{start: start, end: start}, nil
|
||||
case 2:
|
||||
end, err := strconv.ParseInt(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return interval{}, fmt.Errorf("invalid interval (%q): %v", s, err)
|
||||
}
|
||||
return interval{start: start, end: end}, nil
|
||||
default:
|
||||
return interval{}, fmt.Errorf("invalid interval (%q): expected start-end or single number", s)
|
||||
}
|
||||
}
|
||||
|
||||
// parseMysql56GTIDSet is registered as a GTIDSet parser.
|
||||
//
|
||||
// https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-concepts.html
|
||||
func parseMysql56GTIDSet(s string) (GTIDSet, error) {
|
||||
set := Mysql56GTIDSet{}
|
||||
|
||||
// gtid_set: uuid_set [, uuid_set] ...
|
||||
for _, uuidSet := range strings.Split(s, ",") {
|
||||
uuidSet = strings.TrimSpace(uuidSet)
|
||||
if uuidSet == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// uuid_set: uuid:interval[:interval]...
|
||||
parts := strings.Split(uuidSet, ":")
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf("invalid MySQL 5.6 GTID set (%q): expected uuid:interval", s)
|
||||
}
|
||||
|
||||
// Parse Server ID.
|
||||
sid, err := ParseSID(parts[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid MySQL 5.6 GTID set (%q): %v", s, err)
|
||||
}
|
||||
|
||||
// Parse Intervals.
|
||||
intervals := make([]interval, 0, len(parts)-1)
|
||||
for _, part := range parts[1:] {
|
||||
iv, err := parseInterval(part)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid MySQL 5.6 GTID set (%q): %v", s, err)
|
||||
}
|
||||
if iv.end < iv.start {
|
||||
// According to MySQL 5.6 code:
|
||||
// "The end of an interval may be 0, but any interval that has an
|
||||
// endpoint that is smaller than the start is discarded."
|
||||
continue
|
||||
}
|
||||
intervals = append(intervals, iv)
|
||||
}
|
||||
if len(intervals) == 0 {
|
||||
// We might have discarded all the intervals.
|
||||
continue
|
||||
}
|
||||
|
||||
// Internally we expect intervals to be stored in order.
|
||||
sort.Sort(intervalList(intervals))
|
||||
set[sid] = intervals
|
||||
}
|
||||
|
||||
return set, nil
|
||||
}
|
||||
|
||||
// Mysql56GTIDSet implements GTIDSet for MySQL 5.6.
|
||||
type Mysql56GTIDSet map[SID][]interval
|
||||
|
||||
// SIDs returns a sorted list of SIDs in the set.
|
||||
func (set Mysql56GTIDSet) SIDs() []SID {
|
||||
sids := make([]SID, 0, len(set))
|
||||
for sid := range set {
|
||||
sids = append(sids, sid)
|
||||
}
|
||||
sort.Sort(sidList(sids))
|
||||
return sids
|
||||
}
|
||||
|
||||
type sidList []SID
|
||||
|
||||
// Len implements sort.Interface.
|
||||
func (s sidList) Len() int { return len(s) }
|
||||
|
||||
// Less implements sort.Interface.
|
||||
func (s sidList) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 }
|
||||
|
||||
// Swap implements sort.Interface.
|
||||
func (s sidList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// String implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
for i, sid := range set.SIDs() {
|
||||
if i != 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
buf.WriteString(sid.String())
|
||||
|
||||
for _, interval := range set[sid] {
|
||||
buf.WriteByte(':')
|
||||
buf.WriteString(strconv.FormatInt(interval.start, 10))
|
||||
|
||||
if interval.end != interval.start {
|
||||
buf.WriteByte('-')
|
||||
buf.WriteString(strconv.FormatInt(interval.end, 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Flavor implements GTIDSet.
|
||||
func (Mysql56GTIDSet) Flavor() string { return mysql56FlavorID }
|
||||
|
||||
// ContainsGTID implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) ContainsGTID(gtid GTID) bool {
|
||||
gtid56, ok := gtid.(Mysql56GTID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, iv := range set[gtid56.Server] {
|
||||
if iv.start > gtid56.Sequence {
|
||||
// We assume intervals are sorted, so we can skip the rest.
|
||||
return false
|
||||
}
|
||||
if gtid56.Sequence <= iv.end {
|
||||
// Now we know that: start <= Sequence <= end.
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Server wasn't in the set, or no interval contained gtid.
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) Contains(other GTIDSet) bool {
|
||||
other56, ok := other.(Mysql56GTIDSet)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check each SID in the other set.
|
||||
for sid, otherIntervals := range other56 {
|
||||
i := 0
|
||||
intervals := set[sid]
|
||||
count := len(intervals)
|
||||
|
||||
// Check each interval for this SID in the other set.
|
||||
for _, iv := range otherIntervals {
|
||||
// Check that interval against each of our intervals.
|
||||
// Intervals are monotonically increasing,
|
||||
// so we don't need to reset the index each time.
|
||||
for {
|
||||
if i >= count {
|
||||
// We ran out of intervals to check against.
|
||||
return false
|
||||
}
|
||||
if intervals[i].contains(iv) {
|
||||
// Yes it's covered. Go on to the next one.
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No uncovered intervals were found.
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) Equal(other GTIDSet) bool {
|
||||
other56, ok := other.(Mysql56GTIDSet)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for same number of SIDs.
|
||||
if len(set) != len(other56) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare each SID.
|
||||
for sid, intervals := range set {
|
||||
otherIntervals := other56[sid]
|
||||
|
||||
// Check for same number of intervals.
|
||||
if len(intervals) != len(otherIntervals) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Compare each interval.
|
||||
// Since intervals are sorted, they have to be in the same order.
|
||||
for i, iv := range intervals {
|
||||
if iv != otherIntervals[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No discrepancies were found.
|
||||
return true
|
||||
}
|
||||
|
||||
// AddGTID implements GTIDSet.
|
||||
func (set Mysql56GTIDSet) AddGTID(gtid GTID) GTIDSet {
|
||||
gtid56, ok := gtid.(Mysql56GTID)
|
||||
if !ok {
|
||||
return set
|
||||
}
|
||||
|
||||
// Make a copy and add the new GTID in the proper place.
|
||||
// This function is not supposed to modify the original set.
|
||||
newSet := make(Mysql56GTIDSet)
|
||||
|
||||
added := false
|
||||
|
||||
for sid, intervals := range set {
|
||||
newIntervals := make([]interval, 0, len(intervals))
|
||||
|
||||
if sid == gtid56.Server {
|
||||
// Look for the right place to add this GTID.
|
||||
for _, iv := range intervals {
|
||||
if !added {
|
||||
switch {
|
||||
case gtid56.Sequence == iv.start-1:
|
||||
// Expand the interval at the beginning.
|
||||
iv.start = gtid56.Sequence
|
||||
added = true
|
||||
case gtid56.Sequence == iv.end+1:
|
||||
// Expand the interval at the end.
|
||||
iv.end = gtid56.Sequence
|
||||
added = true
|
||||
case gtid56.Sequence < iv.start-1:
|
||||
// The next interval is beyond the new GTID, but it can't
|
||||
// be expanded, so we have to insert a new interval.
|
||||
newIntervals = append(newIntervals, interval{start: gtid56.Sequence, end: gtid56.Sequence})
|
||||
added = true
|
||||
}
|
||||
}
|
||||
// Check if this interval can be merged with the previous one.
|
||||
count := len(newIntervals)
|
||||
if count != 0 && iv.start == newIntervals[count-1].end+1 {
|
||||
// Merge instead of appending.
|
||||
newIntervals[count-1].end = iv.end
|
||||
} else {
|
||||
// Can't be merged.
|
||||
newIntervals = append(newIntervals, iv)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Just copy everything.
|
||||
newIntervals = append(newIntervals, intervals...)
|
||||
}
|
||||
|
||||
newSet[sid] = newIntervals
|
||||
}
|
||||
|
||||
if !added {
|
||||
// There wasn't any place to insert the new GTID, so just append it
|
||||
// as a new interval.
|
||||
newSet[gtid56.Server] = append(newSet[gtid56.Server], interval{start: gtid56.Sequence, end: gtid56.Sequence})
|
||||
}
|
||||
|
||||
return newSet
|
||||
}
|
||||
|
||||
// SIDBlock returns the binary encoding of a MySQL 5.6 GTID set as expected
|
||||
// by internal commands that refer to an "SID block".
|
||||
//
|
||||
// e.g. https://dev.mysql.com/doc/internals/en/com-binlog-dump-gtid.html
|
||||
func (set Mysql56GTIDSet) SIDBlock() []byte {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
// Number of SIDs.
|
||||
binary.Write(buf, binary.LittleEndian, uint64(len(set)))
|
||||
|
||||
for _, sid := range set.SIDs() {
|
||||
buf.Write(sid[:])
|
||||
|
||||
// Number of intervals.
|
||||
intervals := set[sid]
|
||||
binary.Write(buf, binary.LittleEndian, uint64(len(intervals)))
|
||||
|
||||
for _, iv := range intervals {
|
||||
binary.Write(buf, binary.LittleEndian, iv.start)
|
||||
binary.Write(buf, binary.LittleEndian, iv.end)
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func init() {
|
||||
gtidSetParsers[mysql56FlavorID] = parseMysql56GTIDSet
|
||||
}
|
|
@ -0,0 +1,430 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSortSIDList(t *testing.T) {
|
||||
input := []SID{
|
||||
{1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
}
|
||||
want := []SID{
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16},
|
||||
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
{1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
}
|
||||
sort.Sort(sidList(input))
|
||||
if !reflect.DeepEqual(input, want) {
|
||||
t.Errorf("got %#v, want %#v", input, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMysql56GTIDSet(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255}
|
||||
|
||||
table := map[string]Mysql56GTIDSet{
|
||||
// Empty
|
||||
"": Mysql56GTIDSet{},
|
||||
// Simple case
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}},
|
||||
},
|
||||
// Capital hex chars
|
||||
"00010203-0405-0607-0809-0A0B0C0D0E0F:1-5": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}},
|
||||
},
|
||||
// Interval with same start and end
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:12": Mysql56GTIDSet{
|
||||
sid1: []interval{{12, 12}},
|
||||
},
|
||||
// Multiple intervals
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
},
|
||||
// Multiple intervals, out of oder
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:10-20:1-5": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
},
|
||||
// Intervals with end < start are discarded by MySQL 5.6
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:8-7": Mysql56GTIDSet{},
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:8-7:10-20": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
},
|
||||
// Multiple SIDs
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20,00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
sid2: []interval{{1, 5}, {50, 50}},
|
||||
},
|
||||
// Multiple SIDs with space around the comma
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20, 00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
sid2: []interval{{1, 5}, {50, 50}},
|
||||
},
|
||||
}
|
||||
|
||||
for input, want := range table {
|
||||
got, err := parseMysql56GTIDSet(input)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
if !got.Equal(want) {
|
||||
t.Errorf("parseMysql56GTIDSet(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMysql56GTIDSetInvalid(t *testing.T) {
|
||||
table := []string{
|
||||
// No intervals
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f",
|
||||
// Invalid SID
|
||||
"00010203-0405-060X-0809-0a0b0c0d0e0f:1-5",
|
||||
// Invalid intervals
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:0-5",
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:-5",
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-2-3",
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-",
|
||||
}
|
||||
|
||||
for _, input := range table {
|
||||
_, err := parseMysql56GTIDSet(input)
|
||||
if err == nil {
|
||||
t.Errorf("parseMysql56GTIDSet(%#v) expected error, got none", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetString(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 255}
|
||||
|
||||
table := map[string]Mysql56GTIDSet{
|
||||
// Simple case
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}},
|
||||
},
|
||||
// Interval with same start and end
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:12": Mysql56GTIDSet{
|
||||
sid1: []interval{{12, 12}},
|
||||
},
|
||||
// Multiple intervals
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
},
|
||||
// Multiple SIDs
|
||||
"00010203-0405-0607-0809-0a0b0c0d0e0f:1-5:10-20,00010203-0405-0607-0809-0a0b0c0d0eff:1-5:50": Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 5}, {10, 20}},
|
||||
sid2: []interval{{1, 5}, {50, 50}},
|
||||
},
|
||||
}
|
||||
|
||||
for want, input := range table {
|
||||
got := strings.ToLower(input.String())
|
||||
if got != want {
|
||||
t.Errorf("%#v.String() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetFlavor(t *testing.T) {
|
||||
input := Mysql56GTIDSet{}
|
||||
if got, want := input.Flavor(), "MySQL56"; got != want {
|
||||
t.Errorf("%#v.Flavor() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetContainsGTID(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}},
|
||||
}
|
||||
|
||||
table := map[GTID]bool{
|
||||
fakeGTID{}: false,
|
||||
|
||||
Mysql56GTID{sid1, 1}: false,
|
||||
Mysql56GTID{sid1, 19}: false,
|
||||
Mysql56GTID{sid1, 20}: true,
|
||||
Mysql56GTID{sid1, 23}: true,
|
||||
Mysql56GTID{sid1, 30}: true,
|
||||
Mysql56GTID{sid1, 31}: false,
|
||||
|
||||
Mysql56GTID{sid2, 1}: true,
|
||||
Mysql56GTID{sid2, 10}: false,
|
||||
Mysql56GTID{sid2, 50}: true,
|
||||
Mysql56GTID{sid2, 51}: false,
|
||||
|
||||
Mysql56GTID{sid3, 1}: false,
|
||||
}
|
||||
|
||||
for input, want := range table {
|
||||
if got := set.ContainsGTID(input); got != want {
|
||||
t.Errorf("ContainsGTID(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetContains(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
// The set to test against.
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
}
|
||||
|
||||
// Test cases that should return Contains() = true.
|
||||
contained := []Mysql56GTIDSet{
|
||||
// The set should contain itself.
|
||||
set,
|
||||
// Every set contains the empty set.
|
||||
Mysql56GTIDSet{},
|
||||
|
||||
// Simple case
|
||||
Mysql56GTIDSet{sid1: []interval{{25, 30}}},
|
||||
// Multiple intervals
|
||||
Mysql56GTIDSet{sid2: []interval{{1, 2}, {4, 5}, {60, 70}}},
|
||||
// Multiple SIDs
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{25, 30}, {35, 37}},
|
||||
sid2: []interval{{1, 5}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, other := range contained {
|
||||
if !set.Contains(other) {
|
||||
t.Errorf("Contains(%#v) = false, want true", other)
|
||||
}
|
||||
}
|
||||
|
||||
// Test cases that should return Contains() = false.
|
||||
notContained := []GTIDSet{
|
||||
// Wrong flavor is not contained.
|
||||
fakeGTID{},
|
||||
|
||||
// Simple cases
|
||||
Mysql56GTIDSet{sid1: []interval{{1, 5}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{10, 19}}},
|
||||
// Overlapping intervals
|
||||
Mysql56GTIDSet{sid1: []interval{{10, 20}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{10, 25}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{25, 31}}},
|
||||
Mysql56GTIDSet{sid1: []interval{{30, 31}}},
|
||||
// Multiple intervals
|
||||
Mysql56GTIDSet{sid1: []interval{{20, 30}, {34, 34}}},
|
||||
// Multiple SIDs
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {36, 36}},
|
||||
sid2: []interval{{3, 5}, {55, 60}},
|
||||
},
|
||||
// SID is missing entirely
|
||||
Mysql56GTIDSet{sid3: []interval{{1, 5}}},
|
||||
}
|
||||
|
||||
for _, other := range notContained {
|
||||
if set.Contains(other) {
|
||||
t.Errorf("Contains(%#v) = true, want false", other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetEqual(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
// The set to test against.
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
}
|
||||
|
||||
// Test cases that should return Equal() = true.
|
||||
equal := []Mysql56GTIDSet{
|
||||
// Same underlying map instance
|
||||
set,
|
||||
// Different instance, same data
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, other := range equal {
|
||||
if !set.Equal(other) {
|
||||
t.Errorf("%#v.Equal(%#v) = false, want true", set, other)
|
||||
}
|
||||
// Equality should be transitive.
|
||||
if !other.Equal(set) {
|
||||
t.Errorf("%#v.Equal(%#v) = false, want true", other, set)
|
||||
}
|
||||
}
|
||||
|
||||
// Test cases that should return Equal() = false.
|
||||
notEqual := []GTIDSet{
|
||||
// Wrong flavor is not equal.
|
||||
fakeGTID{},
|
||||
// Empty set
|
||||
Mysql56GTIDSet{},
|
||||
// Interval changed
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 31}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Interval added
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {32, 33}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Interval removed
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {60, 70}},
|
||||
},
|
||||
// Different SID, same intervals
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid3: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// SID added
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
sid3: []interval{{1, 5}},
|
||||
},
|
||||
// SID removed
|
||||
Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, other := range notEqual {
|
||||
if set.Equal(other) {
|
||||
t.Errorf("%#v.Equal(%#v) = true, want false", set, other)
|
||||
}
|
||||
// Equality should be transitive.
|
||||
if other.Equal(set) {
|
||||
t.Errorf("%#v.Equal(%#v) = true, want false", other, set)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetAddGTID(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
sid3 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17}
|
||||
|
||||
// The set to test against.
|
||||
set := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
}
|
||||
|
||||
table := map[GTID]Mysql56GTIDSet{
|
||||
// Adding wrong flavor is a no-op.
|
||||
fakeGTID{}: set,
|
||||
|
||||
// New interval beginning
|
||||
Mysql56GTID{Server: sid1, Sequence: 1}: Mysql56GTIDSet{
|
||||
sid1: []interval{{1, 1}, {20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// New interval middle
|
||||
Mysql56GTID{Server: sid1, Sequence: 32}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {32, 32}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// New interval end
|
||||
Mysql56GTID{Server: sid1, Sequence: 50}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}, {50, 50}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Extend interval start
|
||||
Mysql56GTID{Server: sid2, Sequence: 49}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {49, 50}, {60, 70}},
|
||||
},
|
||||
// Extend interval end
|
||||
Mysql56GTID{Server: sid2, Sequence: 51}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 51}, {60, 70}},
|
||||
},
|
||||
// Merge intervals
|
||||
Mysql56GTID{Server: sid1, Sequence: 41}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
},
|
||||
// Different SID
|
||||
Mysql56GTID{Server: sid3, Sequence: 1}: Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}, {42, 45}},
|
||||
sid2: []interval{{1, 5}, {50, 50}, {60, 70}},
|
||||
sid3: []interval{{1, 1}},
|
||||
},
|
||||
}
|
||||
|
||||
for input, want := range table {
|
||||
if got := set.AddGTID(input); !got.Equal(want) {
|
||||
t.Errorf("AddGTID(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDSetSIDBlock(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
sid2 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16}
|
||||
|
||||
input := Mysql56GTIDSet{
|
||||
sid1: []interval{{20, 30}, {35, 40}},
|
||||
sid2: []interval{{1, 5}},
|
||||
}
|
||||
want := []byte{
|
||||
// n_sids
|
||||
2, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
// sid1: n_intervals
|
||||
2, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 1 start
|
||||
20, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 1 end
|
||||
30, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 2 start
|
||||
35, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid1: interval 2 end
|
||||
40, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid2
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16,
|
||||
// sid2: n_intervals
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid2: interval 1 start
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
// sid2: interval 1 end
|
||||
5, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
if got := input.SIDBlock(); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%#v.SIDBlock() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
// Copyright 2015, Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseMysql56GTID(t *testing.T) {
|
||||
input := "00010203-0405-0607-0809-0A0B0C0D0E0F:56789"
|
||||
want := Mysql56GTID{
|
||||
Server: SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
Sequence: 56789,
|
||||
}
|
||||
|
||||
got, err := parseMysql56GTID(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("parseMysql56GTID(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseMysql56GTIDInvalid(t *testing.T) {
|
||||
table := []string{
|
||||
"",
|
||||
"00010203-0405-0607-0809-0A0B0C0D0E0F",
|
||||
"00010203-0405-0607-0809-0A0B0C0D0E0F:1-5",
|
||||
"00010203-0405-0607-0809-0A0B0C0D0E0F:1:2",
|
||||
"00010203-0405-0607-0809-0A0B0C0D0E0X:1",
|
||||
}
|
||||
|
||||
for _, input := range table {
|
||||
_, err := parseMysql56GTID(input)
|
||||
if err == nil {
|
||||
t.Errorf("parseMysql56GTID(%#v): expected error, got none", input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSIDString(t *testing.T) {
|
||||
input := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
want := "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
if got := strings.ToLower(input.String()); got != want {
|
||||
t.Errorf("%#v.String() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSID(t *testing.T) {
|
||||
input := "00010203-0405-0607-0809-0A0B0C0D0E0F"
|
||||
want := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
|
||||
got, err := ParseSID(input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("ParseSID(%#v) = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSIDInvalid(t *testing.T) {
|
||||
table := []string{
|
||||
"123",
|
||||
"x",
|
||||
"00010203-0405-0607-0809-0A0B0C0D0E0x",
|
||||
"00010203-0405-0607-080900A0B0C0D0E0F",
|
||||
}
|
||||
|
||||
for _, input := range table {
|
||||
_, err := ParseSID(input)
|
||||
if err == nil {
|
||||
t.Errorf("ParseSID(%#v): expected error, got none", input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDString(t *testing.T) {
|
||||
input := Mysql56GTID{
|
||||
Server: SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
Sequence: 12345,
|
||||
}
|
||||
want := "00010203-0405-0607-0809-0a0b0c0d0e0f:12345"
|
||||
if got := strings.ToLower(input.String()); got != want {
|
||||
t.Errorf("%#v.String() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDFlavor(t *testing.T) {
|
||||
input := Mysql56GTID{}
|
||||
if got, want := input.Flavor(), "MySQL56"; got != want {
|
||||
t.Errorf("%#v.Flavor() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56SequenceDomain(t *testing.T) {
|
||||
input := Mysql56GTID{}
|
||||
if got, want := input.SequenceDomain(), interface{}(nil); got != want {
|
||||
t.Errorf("%#v.SequenceDomain() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56SourceServer(t *testing.T) {
|
||||
input := Mysql56GTID{
|
||||
Server: SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
}
|
||||
want := interface{}(SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
|
||||
if got := input.SourceServer(); got != want {
|
||||
t.Errorf("%#v.SourceServer() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56SequenceNumber(t *testing.T) {
|
||||
input := Mysql56GTID{
|
||||
Server: SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
Sequence: 5432,
|
||||
}
|
||||
want := interface{}(int64(5432))
|
||||
if got := input.SequenceNumber(); got != want {
|
||||
t.Errorf("%#v.SequenceNumber() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMysql56GTIDGTIDSet(t *testing.T) {
|
||||
sid1 := SID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
input := Mysql56GTID{Server: sid1, Sequence: 5432}
|
||||
want := Mysql56GTIDSet{sid1: []interval{{5432, 5432}}}
|
||||
if got := input.GTIDSet(); !got.Equal(want) {
|
||||
t.Errorf("%#v.GTIDSet() = %#v, want %#v", input, got, want)
|
||||
}
|
||||
}
|
|
@ -126,6 +126,9 @@ class MySQL56(MysqlFlavor):
|
|||
def bootstrap_archive(self):
|
||||
return "mysql-db-dir_5.6.24.tbz"
|
||||
|
||||
def extra_my_cnf(self):
|
||||
return environment.vttop + "/config/mycnf/master_mysql56.cnf"
|
||||
|
||||
__mysql_flavor = None
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче