diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b1ed2a4 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/soniah/mysql-go + +go 1.12 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/tests/.golangci.yml b/tests/.golangci.yml new file mode 100644 index 0000000..86e9a36 --- /dev/null +++ b/tests/.golangci.yml @@ -0,0 +1,9 @@ +linters-settings: + gocyclo: + min-complexity: 10 + +linters: + enable-all: true + disable: + - maligned + - depguard diff --git a/tests/db_test.go b/tests/db_test.go new file mode 100644 index 0000000..1e8962b --- /dev/null +++ b/tests/db_test.go @@ -0,0 +1,192 @@ +package tests + +import ( + "bytes" + "context" + "database/sql" + "fmt" + "io/ioutil" + "log" + "os" + "sync" + "testing" + "text/tabwriter" + "time" + + "github.com/go-sql-driver/mysql" + "github.com/jmoiron/sqlx" + "github.com/ory/dockertest" + mysqlgo "github.com/soniah/mysql-go" + "github.com/stretchr/testify/assert" +) + +// nolint:gochecknoglobals +var dockerPool *dockertest.Pool // the connection to docker +// nolint:gochecknoglobals +var systemdb *sql.DB // the connection to the mysql 'system' database +// nolint:gochecknoglobals +var sqlConfig *mysql.Config // the mysql container and config for connecting to other databases +// nolint:gochecknoglobals +var testMu *sync.Mutex // controls access to sqlConfig + +func TestMain(m *testing.M) { + _ = mysql.SetLogger(log.New(ioutil.Discard, "", 0)) // silence mysql logger + testMu = &sync.Mutex{} + + var err error + dockerPool, err = dockertest.NewPool("") + if err != nil { + log.Fatalf("could not connect to docker: %s", err) + } + dockerPool.MaxWait = time.Minute * 2 + + runOptions := dockertest.RunOptions{ + Repository: "mysql", + Tag: "5.6", + Env: []string{"MYSQL_ROOT_PASSWORD=secret"}, + } + mysqlContainer, err := dockerPool.RunWithOptions(&runOptions) + if err != nil { + log.Fatalf("could not start mysqlContainer: %s", err) + } + + sqlConfig = &mysql.Config{ + User: "root", + Passwd: "secret", + Net: "tcp", + Addr: fmt.Sprintf("localhost:%s", mysqlContainer.GetPort("3306/tcp")), + DBName: "mysql", + AllowNativePasswords: true, + } + + if err = dockerPool.Retry(func() error { + systemdb, err = sql.Open("mysql", sqlConfig.FormatDSN()) + if err != nil { + return err + } + return systemdb.Ping() + }); err != nil { + log.Fatal(err) + } + + code := m.Run() + + // You can't defer this because os.Exit ignores defer + if err := dockerPool.Purge(mysqlContainer); err != nil { + log.Fatalf("Could not purge resource: %s", err) + } + + os.Exit(code) +} + +func TestCancel(t *testing.T) { + var err error + _, err = systemdb.Exec("create database TestCancel") + assert.NoError(t, err) + + testMu.Lock() + testCancelConfig := sqlConfig + testMu.Unlock() + testCancelConfig.DBName = "TestCancel" + var dbStd *sql.DB + if err := dockerPool.Retry(func() error { + dbStd, err = sql.Open("mysql", testCancelConfig.FormatDSN()) + if err != nil { + return err + } + return dbStd.Ping() + }); err != nil { + log.Fatal(err) + } + + dbKiller, err := sql.Open("mysql", testCancelConfig.FormatDSN()) + dbKiller.SetMaxOpenConns(1) + pool := &mysqlgo.DB{DB: dbStd, KillerPool: dbKiller} + + procs, err := helperFullProcessList(dbStd) + assert.NoError(t, err) + + filterDB := func(m mySQLProcInfo) bool { return m.DB == "TestCancel" } + filterState := func(m mySQLProcInfo) bool { return m.State == "executing" } + procs = procs.Filter(filterDB, filterState) + assert.Len(t, procs, 0) + + ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond) + defer cancel() + conn, err := pool.Conn(ctx) + assert.NoError(t, err) + defer conn.Close() + + go func() { + _, err = conn.ExecContext(ctx, "select benchmark(9999999999, md5('I like traffic lights'))") + assert.Equal(t, context.DeadlineExceeded, err) + }() + + ticker := time.NewTicker(100 * time.Millisecond) +Loop: + for { + select { + case <-ticker.C: + procs, err := helperFullProcessList(dbStd) + assert.NoError(t, err) + procs = procs.Filter(filterDB, filterState) + assert.Len(t, procs, 1) + case <-ctx.Done(): + time.Sleep(3000 * time.Millisecond) + procs, err := helperFullProcessList(dbStd) + assert.NoError(t, err) + procs = procs.Filter(filterDB, filterState) + assert.Len(t, procs, 0) + break Loop + } + } +} + +type mySQLProcInfo struct { + ID int64 `db:"Id"` + User string `db:"User"` + Host string `db:"Host"` + DB string `db:"db"` + Command string `db:"Command"` + Time int `db:"Time"` + State string `db:"State"` + Info *string `db:"Info"` +} +type mySQLProcsInfo []mySQLProcInfo + +func helperFullProcessList(db *sql.DB) (mySQLProcsInfo, error) { + dbx := sqlx.NewDb(db, "mysql") + var procs []mySQLProcInfo + if err := dbx.Select(&procs, "show full processlist"); err != nil { + return nil, err + } + return procs, nil +} + +func (ms mySQLProcsInfo) String() string { + var buf bytes.Buffer + w := tabwriter.NewWriter(&buf, 0, 8, 1, '\t', 0) + fmt.Fprintln(w, "ID\tUser\tHost\tDB\tCommand\tTime\tState\tInfo") + for _, m := range ms { + fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", m.ID, m.User, m.Host, m.DB, m.Command, m.Time, + m.State, m.Info) + } + w.Flush() + return buf.String() +} + +func (ms mySQLProcsInfo) Filter(fns ...func(m mySQLProcInfo) bool) (result mySQLProcsInfo) { + for _, m := range ms { + ok := true + for _, fn := range fns { + if !fn(m) { + ok = false + break + } + } + if ok { + result = append(result, m) + } + } + return result +} diff --git a/tests/go.mod b/tests/go.mod new file mode 100644 index 0000000..b074db3 --- /dev/null +++ b/tests/go.mod @@ -0,0 +1,29 @@ +module github.com/soniah/mysql-go/tests + +go 1.12 + +require ( + github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/Microsoft/go-winio v0.4.14 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect + github.com/cenkalti/backoff v2.2.1+incompatible // indirect + github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.4.0 // indirect + github.com/go-sql-driver/mysql v1.4.1 + github.com/google/go-cmp v0.3.1 // indirect + github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect + github.com/jmoiron/sqlx v1.2.0 + github.com/lib/pq v1.2.0 // indirect + github.com/opencontainers/go-digest v1.0.0-rc1 // indirect + github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/runc v0.1.1 // indirect + github.com/ory/dockertest v3.3.4+incompatible + github.com/sirupsen/logrus v1.4.2 // indirect + github.com/soniah/mysql-go v1.0.0 + github.com/stretchr/testify v1.4.0 + golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect + golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect + google.golang.org/appengine v1.6.1 // indirect + gotest.tools v2.2.0+incompatible // indirect +) diff --git a/tests/go.sum b/tests/go.sum new file mode 100644 index 0000000..4b3955e --- /dev/null +++ b/tests/go.sum @@ -0,0 +1,84 @@ +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02 h1:tN9D97v5A5QuKdcKHKt+UMKrkQ5YXUnD8iM7IAAjEfI= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8= +github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soniah/mysql-go v1.0.0 h1:A0CJG3sroPDcb5vSvMn8M1qBQgBv+Y3xPdsC5Dg1r34= +github.com/soniah/mysql-go v1.0.0/go.mod h1:tpqaAsvVj765OmVI3C9IQNsHJEft42h5E2wZkhP3E4Y= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=