2017-02-12 11:13:54 +00:00
|
|
|
package canal
|
|
|
|
|
|
|
|
import (
|
2019-01-01 08:57:46 +00:00
|
|
|
"bytes"
|
2017-02-12 11:13:54 +00:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"testing"
|
2019-01-01 08:57:46 +00:00
|
|
|
"time"
|
2017-02-12 11:13:54 +00:00
|
|
|
|
2019-01-01 08:57:46 +00:00
|
|
|
"github.com/juju/errors"
|
2017-02-12 11:13:54 +00:00
|
|
|
. "github.com/pingcap/check"
|
2019-01-01 08:57:46 +00:00
|
|
|
"github.com/siddontang/go-log/log"
|
2017-02-12 11:13:54 +00:00
|
|
|
"github.com/siddontang/go-mysql/mysql"
|
|
|
|
)
|
|
|
|
|
|
|
|
var testHost = flag.String("host", "127.0.0.1", "MySQL host")
|
|
|
|
|
|
|
|
func Test(t *testing.T) {
|
|
|
|
TestingT(t)
|
|
|
|
}
|
|
|
|
|
|
|
|
type canalTestSuite struct {
|
|
|
|
c *Canal
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ = Suite(&canalTestSuite{})
|
|
|
|
|
|
|
|
func (s *canalTestSuite) SetUpSuite(c *C) {
|
|
|
|
cfg := NewDefaultConfig()
|
|
|
|
cfg.Addr = fmt.Sprintf("%s:3306", *testHost)
|
|
|
|
cfg.User = "root"
|
2019-01-01 08:57:46 +00:00
|
|
|
cfg.HeartbeatPeriod = 200 * time.Millisecond
|
|
|
|
cfg.ReadTimeout = 300 * time.Millisecond
|
2017-02-12 11:13:54 +00:00
|
|
|
cfg.Dump.ExecutionPath = "mysqldump"
|
|
|
|
cfg.Dump.TableDB = "test"
|
|
|
|
cfg.Dump.Tables = []string{"canal_test"}
|
2019-01-01 08:57:46 +00:00
|
|
|
cfg.Dump.Where = "id>0"
|
2017-02-12 11:13:54 +00:00
|
|
|
|
2019-01-01 08:57:46 +00:00
|
|
|
// include & exclude config
|
|
|
|
cfg.IncludeTableRegex = make([]string, 1)
|
|
|
|
cfg.IncludeTableRegex[0] = ".*\\.canal_test"
|
|
|
|
cfg.ExcludeTableRegex = make([]string, 2)
|
|
|
|
cfg.ExcludeTableRegex[0] = "mysql\\..*"
|
|
|
|
cfg.ExcludeTableRegex[1] = ".*\\..*_inner"
|
2017-02-12 11:13:54 +00:00
|
|
|
|
|
|
|
var err error
|
|
|
|
s.c, err = NewCanal(cfg)
|
|
|
|
c.Assert(err, IsNil)
|
2019-01-01 08:57:46 +00:00
|
|
|
s.execute(c, "DROP TABLE IF EXISTS test.canal_test")
|
2017-02-12 11:13:54 +00:00
|
|
|
sql := `
|
|
|
|
CREATE TABLE IF NOT EXISTS test.canal_test (
|
2019-01-01 08:57:46 +00:00
|
|
|
id int AUTO_INCREMENT,
|
|
|
|
content blob DEFAULT NULL,
|
2017-02-12 11:13:54 +00:00
|
|
|
name varchar(100),
|
|
|
|
PRIMARY KEY(id)
|
|
|
|
)ENGINE=innodb;
|
|
|
|
`
|
|
|
|
|
|
|
|
s.execute(c, sql)
|
|
|
|
|
|
|
|
s.execute(c, "DELETE FROM test.canal_test")
|
2019-01-01 08:57:46 +00:00
|
|
|
s.execute(c, "INSERT INTO test.canal_test (content, name) VALUES (?, ?), (?, ?), (?, ?)", "1", "a", `\0\ndsfasdf`, "b", "", "c")
|
2017-02-12 11:13:54 +00:00
|
|
|
|
|
|
|
s.execute(c, "SET GLOBAL binlog_format = 'ROW'")
|
|
|
|
|
2019-01-01 08:57:46 +00:00
|
|
|
s.c.SetEventHandler(&testEventHandler{c: c})
|
|
|
|
go func() {
|
|
|
|
err = s.c.Run()
|
|
|
|
c.Assert(err, IsNil)
|
|
|
|
}()
|
2017-02-12 11:13:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *canalTestSuite) TearDownSuite(c *C) {
|
2019-01-01 08:57:46 +00:00
|
|
|
// To test the heartbeat and read timeout,so need to sleep 1 seconds without data transmission
|
|
|
|
c.Logf("Start testing the heartbeat and read timeout")
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
2017-02-12 11:13:54 +00:00
|
|
|
if s.c != nil {
|
|
|
|
s.c.Close()
|
|
|
|
s.c = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *canalTestSuite) execute(c *C, query string, args ...interface{}) *mysql.Result {
|
|
|
|
r, err := s.c.Execute(query, args...)
|
|
|
|
c.Assert(err, IsNil)
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2019-01-01 08:57:46 +00:00
|
|
|
type testEventHandler struct {
|
|
|
|
DummyEventHandler
|
|
|
|
|
|
|
|
c *C
|
2017-02-12 11:13:54 +00:00
|
|
|
}
|
|
|
|
|
2019-01-01 08:57:46 +00:00
|
|
|
func (h *testEventHandler) OnRow(e *RowsEvent) error {
|
|
|
|
log.Infof("OnRow %s %v\n", e.Action, e.Rows)
|
2017-02-12 11:13:54 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-01 08:57:46 +00:00
|
|
|
func (h *testEventHandler) String() string {
|
|
|
|
return "testEventHandler"
|
2017-02-12 11:13:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *canalTestSuite) TestCanal(c *C) {
|
|
|
|
<-s.c.WaitDumpDone()
|
|
|
|
|
|
|
|
for i := 1; i < 10; i++ {
|
|
|
|
s.execute(c, "INSERT INTO test.canal_test (name) VALUES (?)", fmt.Sprintf("%d", i))
|
|
|
|
}
|
2019-01-01 08:57:46 +00:00
|
|
|
s.execute(c, "ALTER TABLE test.canal_test ADD `age` INT(5) NOT NULL AFTER `name`")
|
|
|
|
s.execute(c, "INSERT INTO test.canal_test (name,age) VALUES (?,?)", "d", "18")
|
|
|
|
|
|
|
|
err := s.c.CatchMasterPos(10 * time.Second)
|
|
|
|
c.Assert(err, IsNil)
|
|
|
|
}
|
2017-02-12 11:13:54 +00:00
|
|
|
|
2019-01-01 08:57:46 +00:00
|
|
|
func (s *canalTestSuite) TestCanalFilter(c *C) {
|
|
|
|
// included
|
|
|
|
sch, err := s.c.GetTable("test", "canal_test")
|
2017-02-12 11:13:54 +00:00
|
|
|
c.Assert(err, IsNil)
|
2019-01-01 08:57:46 +00:00
|
|
|
c.Assert(sch, NotNil)
|
|
|
|
_, err = s.c.GetTable("not_exist_db", "canal_test")
|
|
|
|
c.Assert(errors.Trace(err), Not(Equals), ErrExcludedTable)
|
|
|
|
// excluded
|
|
|
|
sch, err = s.c.GetTable("test", "canal_test_inner")
|
|
|
|
c.Assert(errors.Cause(err), Equals, ErrExcludedTable)
|
|
|
|
c.Assert(sch, IsNil)
|
|
|
|
sch, err = s.c.GetTable("mysql", "canal_test")
|
|
|
|
c.Assert(errors.Cause(err), Equals, ErrExcludedTable)
|
|
|
|
c.Assert(sch, IsNil)
|
|
|
|
sch, err = s.c.GetTable("not_exist_db", "not_canal_test")
|
|
|
|
c.Assert(errors.Cause(err), Equals, ErrExcludedTable)
|
|
|
|
c.Assert(sch, IsNil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCreateTableExp(t *testing.T) {
|
|
|
|
cases := []string{
|
|
|
|
"CREATE TABLE `mydb.mytable` (`id` int(10)) ENGINE=InnoDB",
|
|
|
|
"CREATE TABLE `mytable` (`id` int(10)) ENGINE=InnoDB",
|
|
|
|
"CREATE TABLE IF NOT EXISTS `mytable` (`id` int(10)) ENGINE=InnoDB",
|
|
|
|
"CREATE TABLE IF NOT EXISTS mytable (`id` int(10)) ENGINE=InnoDB",
|
|
|
|
}
|
|
|
|
table := []byte("mytable")
|
|
|
|
db := []byte("mydb")
|
|
|
|
for _, s := range cases {
|
|
|
|
m := expCreateTable.FindSubmatch([]byte(s))
|
|
|
|
mLen := len(m)
|
|
|
|
if m == nil || !bytes.Equal(m[mLen-1], table) || (len(m[mLen-2]) > 0 && !bytes.Equal(m[mLen-2], db)) {
|
|
|
|
t.Fatalf("TestCreateTableExp: case %s failed\n", s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAlterTableExp(t *testing.T) {
|
|
|
|
cases := []string{
|
|
|
|
"ALTER TABLE `mydb`.`mytable` ADD `field2` DATE NULL AFTER `field1`;",
|
|
|
|
"ALTER TABLE `mytable` ADD `field2` DATE NULL AFTER `field1`;",
|
|
|
|
"ALTER TABLE mydb.mytable ADD `field2` DATE NULL AFTER `field1`;",
|
|
|
|
"ALTER TABLE mytable ADD `field2` DATE NULL AFTER `field1`;",
|
|
|
|
"ALTER TABLE mydb.mytable ADD field2 DATE NULL AFTER `field1`;",
|
|
|
|
}
|
|
|
|
|
|
|
|
table := []byte("mytable")
|
|
|
|
db := []byte("mydb")
|
|
|
|
for _, s := range cases {
|
|
|
|
m := expAlterTable.FindSubmatch([]byte(s))
|
|
|
|
mLen := len(m)
|
|
|
|
if m == nil || !bytes.Equal(m[mLen-1], table) || (len(m[mLen-2]) > 0 && !bytes.Equal(m[mLen-2], db)) {
|
|
|
|
t.Fatalf("TestAlterTableExp: case %s failed\n", s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRenameTableExp(t *testing.T) {
|
|
|
|
cases := []string{
|
|
|
|
"rename table `mydb`.`mytable` to `mydb`.`mytable1`",
|
|
|
|
"rename table `mytable` to `mytable1`",
|
|
|
|
"rename table mydb.mytable to mydb.mytable1",
|
|
|
|
"rename table mytable to mytable1",
|
|
|
|
|
|
|
|
"rename table `mydb`.`mytable` to `mydb`.`mytable2`, `mydb`.`mytable3` to `mydb`.`mytable1`",
|
|
|
|
"rename table `mytable` to `mytable2`, `mytable3` to `mytable1`",
|
|
|
|
"rename table mydb.mytable to mydb.mytable2, mydb.mytable3 to mydb.mytable1",
|
|
|
|
"rename table mytable to mytable2, mytable3 to mytable1",
|
|
|
|
}
|
|
|
|
table := []byte("mytable")
|
|
|
|
db := []byte("mydb")
|
|
|
|
for _, s := range cases {
|
|
|
|
m := expRenameTable.FindSubmatch([]byte(s))
|
|
|
|
mLen := len(m)
|
|
|
|
if m == nil || !bytes.Equal(m[mLen-1], table) || (len(m[mLen-2]) > 0 && !bytes.Equal(m[mLen-2], db)) {
|
|
|
|
t.Fatalf("TestRenameTableExp: case %s failed\n", s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDropTableExp(t *testing.T) {
|
|
|
|
cases := []string{
|
|
|
|
"drop table test1",
|
|
|
|
"DROP TABLE test1",
|
|
|
|
"DROP TABLE test1",
|
|
|
|
"DROP table IF EXISTS test.test1",
|
|
|
|
"drop table `test1`",
|
|
|
|
"DROP TABLE `test1`",
|
|
|
|
"DROP table IF EXISTS `test`.`test1`",
|
|
|
|
"DROP TABLE `test1` /* generated by server */",
|
|
|
|
"DROP table if exists test1",
|
|
|
|
"DROP table if exists `test1`",
|
|
|
|
"DROP table if exists test.test1",
|
|
|
|
"DROP table if exists `test`.test1",
|
|
|
|
"DROP table if exists `test`.`test1`",
|
|
|
|
"DROP table if exists test.`test1`",
|
|
|
|
"DROP table if exists test.`test1`",
|
|
|
|
}
|
|
|
|
|
|
|
|
table := []byte("test1")
|
|
|
|
for _, s := range cases {
|
|
|
|
m := expDropTable.FindSubmatch([]byte(s))
|
|
|
|
mLen := len(m)
|
|
|
|
if m == nil {
|
|
|
|
t.Fatalf("TestDropTableExp: case %s failed\n", s)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if mLen < 4 {
|
|
|
|
t.Fatalf("TestDropTableExp: case %s failed\n", s)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !bytes.Equal(m[mLen-1], table) {
|
|
|
|
t.Fatalf("TestDropTableExp: case %s failed\n", s)
|
|
|
|
}
|
|
|
|
}
|
2017-02-12 11:13:54 +00:00
|
|
|
}
|