From 96f761ab9a6fc05b231c8673d75c76886f9f0c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Sun, 27 Oct 2024 11:19:15 +0100 Subject: [PATCH] server: Improve example 1. Use the example from the `README.md` and turn it into a `go-mysqlserver` binary that users can run. 2. Add more logging to make it easier to understand what it is doing. This is done both in `go-mysqlserver` as well as the `EmptyHandler` 3. Remove the `server/example` as we already have a example now. 4. Support the minimal set of queries that MySQL Shell `mysqlsh` needs to be able to connect. (tested with MySQL Shell 9.1.0) 5. Change the default version from 5.7.0 to 8.0.11 (first 8.0 GA version) --- Makefile | 1 + README.md | 1 - cmd/go-mysqlserver/main.go | 44 +++++++++++++++++++++++++ server/command.go | 34 ++++++++++++++++++++ server/example/server_example.go | 55 -------------------------------- server/server_conf.go | 2 +- 6 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 cmd/go-mysqlserver/main.go delete mode 100644 server/example/server_example.go diff --git a/Makefile b/Makefile index 90930f3b2..87795751f 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ build: ${GO} build -o bin/go-mysqldump cmd/go-mysqldump/main.go ${GO} build -o bin/go-canal cmd/go-canal/main.go ${GO} build -o bin/go-binlogparser cmd/go-binlogparser/main.go + ${GO} build -o bin/go-mysqlserver cmd/go-mysqlserver/main.go test: ${GO} test --race -timeout 2m ./... diff --git a/README.md b/README.md index 8d9dc1777..7790c88b2 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,6 @@ func main() { } } } - ``` Another shell diff --git a/cmd/go-mysqlserver/main.go b/cmd/go-mysqlserver/main.go new file mode 100644 index 000000000..b14360ab9 --- /dev/null +++ b/cmd/go-mysqlserver/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "log" + "net" + + "github.com/go-mysql-org/go-mysql/server" +) + +func main() { + // Listen for connections on localhost port 4000 + l, err := net.Listen("tcp", "127.0.0.1:4000") + if err != nil { + log.Fatal(err) + } + + log.Println("Listening on port 4000, connect with 'mysql -h 127.0.0.1 -P 4000 -u root'") + + // Accept a new connection once + c, err := l.Accept() + if err != nil { + log.Fatal(err) + } + + log.Println("Accepted connection") + + // Create a connection with user root and an empty password. + // You can use your own handler to handle command here. + conn, err := server.NewConn(c, "root", "", server.EmptyHandler{}) + if err != nil { + log.Fatal(err) + } + + log.Println("Registered the connection with the server") + + // as long as the client keeps sending commands, keep handling them + for { + if err := conn.HandleCommand(); err != nil { + log.Fatal(err) + } + } + + log.Println("Connection is gone, stopping the server") +} diff --git a/server/command.go b/server/command.go index 78a0ea03d..65a1fe38e 100644 --- a/server/command.go +++ b/server/command.go @@ -3,7 +3,9 @@ package server import ( "bytes" "fmt" + "log" + "github.com/go-mysql-org/go-mysql/mysql" . "github.com/go-mysql-org/go-mysql/mysql" "github.com/go-mysql-org/go-mysql/replication" "github.com/siddontang/go/hack" @@ -186,39 +188,71 @@ type EmptyReplicationHandler struct { } func (h EmptyHandler) UseDB(dbName string) error { + log.Printf("Received: UseDB %s", dbName) return nil } func (h EmptyHandler) HandleQuery(query string) (*Result, error) { + log.Printf("Received: Query: %s", query) + + // These two queries are implemented for minimal support for MySQL Shell + if query == `SET NAMES 'utf8mb4';` { + return nil, nil + } + if query == `select concat(@@version, ' ', @@version_comment)` { + r, err := mysql.BuildSimpleResultset([]string{"concat(@@version, ' ', @@version_comment)"}, [][]interface{}{ + {"8.0.11"}, + }, false) + if err != nil { + return nil, err + } else { + return &mysql.Result{ + Status: 0, + Warnings: 0, + InsertId: 0, + AffectedRows: 0, + Resultset: r, + }, nil + } + } + return nil, fmt.Errorf("not supported now") } func (h EmptyHandler) HandleFieldList(table string, fieldWildcard string) ([]*Field, error) { + log.Println("Received: FieldList") return nil, fmt.Errorf("not supported now") } func (h EmptyHandler) HandleStmtPrepare(query string) (int, int, interface{}, error) { + log.Printf("Received: StmtPrepare: %s", query) return 0, 0, nil, fmt.Errorf("not supported now") } func (h EmptyHandler) HandleStmtExecute(context interface{}, query string, args []interface{}) (*Result, error) { + log.Println("Received: StmtExecute") return nil, fmt.Errorf("not supported now") } func (h EmptyHandler) HandleStmtClose(context interface{}) error { + log.Println("Received: StmtClose") return nil } func (h EmptyReplicationHandler) HandleRegisterSlave(data []byte) error { + log.Println("Received: RegisterSlave") return fmt.Errorf("not supported now") } func (h EmptyReplicationHandler) HandleBinlogDump(pos Position) (*replication.BinlogStreamer, error) { + log.Println("Received: BinlogDump") return nil, fmt.Errorf("not supported now") } func (h EmptyReplicationHandler) HandleBinlogDumpGTID(gtidSet *MysqlGTIDSet) (*replication.BinlogStreamer, error) { + log.Println("Received: BinlogDumpGTID") return nil, fmt.Errorf("not supported now") } func (h EmptyHandler) HandleOtherCommand(cmd byte, data []byte) error { + log.Println("Received: OtherCommand") return NewError( ER_UNKNOWN_ERROR, fmt.Sprintf("command %d is not supported now", cmd), diff --git a/server/example/server_example.go b/server/example/server_example.go deleted file mode 100644 index 2af545566..000000000 --- a/server/example/server_example.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "net" - - "github.com/go-mysql-org/go-mysql/mysql" - "github.com/go-mysql-org/go-mysql/server" - "github.com/go-mysql-org/go-mysql/test_util/test_keys" - "github.com/siddontang/go-log/log" - - "crypto/tls" - "time" -) - -type RemoteThrottleProvider struct { - *server.InMemoryProvider - delay int // in milliseconds -} - -func (m *RemoteThrottleProvider) GetCredential(username string) (password string, found bool, err error) { - time.Sleep(time.Millisecond * time.Duration(m.delay)) - return m.InMemoryProvider.GetCredential(username) -} - -func main() { - l, _ := net.Listen("tcp", "127.0.0.1:3306") - // user either the in-memory credential provider or the remote credential provider (you can implement your own) - //inMemProvider := server.NewInMemoryProvider() - //inMemProvider.AddUser("root", "123") - remoteProvider := &RemoteThrottleProvider{server.NewInMemoryProvider(), 10 + 50} - remoteProvider.AddUser("root", "123") - var tlsConf = server.NewServerTLSConfig(test_keys.CaPem, test_keys.CertPem, test_keys.KeyPem, tls.VerifyClientCertIfGiven) - for { - c, _ := l.Accept() - go func() { - // Create a connection with user root and an empty password. - // You can use your own handler to handle command here. - svr := server.NewServer("8.0.12", mysql.DEFAULT_COLLATION_ID, mysql.AUTH_CACHING_SHA2_PASSWORD, test_keys.PubPem, tlsConf) - conn, err := server.NewCustomizedConn(c, svr, remoteProvider, server.EmptyHandler{}) - - if err != nil { - log.Errorf("Connection error: %v", err) - return - } - - for { - err = conn.HandleCommand() - if err != nil { - log.Errorf(`Could not handle command: %v`, err) - return - } - } - }() - } -} diff --git a/server/server_conf.go b/server/server_conf.go index 0328bbe95..bff1967c9 100644 --- a/server/server_conf.go +++ b/server/server_conf.go @@ -48,7 +48,7 @@ func NewDefaultServer() *Server { certPem, keyPem := generateAndSignRSACerts(caPem, caKey) tlsConf := NewServerTLSConfig(caPem, certPem, keyPem, tls.VerifyClientCertIfGiven) return &Server{ - serverVersion: "5.7.0", + serverVersion: "8.0.11", protocolVersion: 10, capability: CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 | CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH | CLIENT_SSL | CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA,