Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

server: DBeaver hangs on SELECT DATABASE() response #985

Open
dveeden opened this issue Feb 7, 2025 · 5 comments
Open

server: DBeaver hangs on SELECT DATABASE() response #985

dveeden opened this issue Feb 7, 2025 · 5 comments

Comments

@dveeden
Copy link
Collaborator

dveeden commented Feb 7, 2025

@rfyim Did you ever get this to work?

I happen to be doing the same thing as you, the same way. Mine is hanging on select database() though sent from dbeaver.

func (h ProxyRequestHandler) HandleQuery(query string) (*mysql.Result, error) {
	log.Println("In HandleQuery")
	log.Println(query)
	r, err := h.remoteDb.Execute(query)
	if err != nil {
		return nil, err
	}
	log.Printf("Executed query: %d", r.AffectedRows)
	return r, nil
}

Originally posted by @matttm in #915

@dveeden dveeden self-assigned this Feb 7, 2025
@dveeden dveeden added the server label Feb 7, 2025
@dveeden
Copy link
Collaborator Author

dveeden commented Feb 7, 2025

Tested with DBeaver CE 24.3.4.202502021521 and MySQL Connector/J 8.2.0

@dveeden
Copy link
Collaborator Author

dveeden commented Feb 7, 2025

So this isn't a bug.

Image

The example is only setup to handle a single connection and DBeaver uses multiple.

It works with this code:

package main

import (
	"fmt"
	"net"
	"strings"

	"github.com/go-mysql-org/go-mysql/client"
	"github.com/go-mysql-org/go-mysql/mysql"
	"github.com/go-mysql-org/go-mysql/server"
)

type ServerHandler struct {
	Conn *client.Conn
}

type Config struct {
	Host     string
	Port     int
	User     string
	Password string
}

func NewServerHandler(cfg Config) (*ServerHandler, error) {
	conn, err := client.Connect(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), cfg.User, cfg.Password, "")
	if err != nil {
		return nil, err
	}
	if err = conn.Ping(); err != nil {
		return nil, err
	}
	return &ServerHandler{
		Conn: conn,
	}, nil
}

func (h ServerHandler) UseDB(dbName string) error {
	return h.Conn.UseDB(dbName)
}

func (h ServerHandler) HandleQuery(query string) (*mysql.Result, error) {
	res, err := h.Conn.Execute(query)

	// SET doesn't seem to work due to EOF/OK differences
	if strings.Contains(query, `SET NAMES`) {
		return nil, nil
	}
	if strings.Contains(query, `set autocommit=0`) {
		return nil, nil
	}
	return res, err
}

func (h ServerHandler) HandleFieldList(table string, fieldWildcard string) ([]*mysql.Field, error) {
	return h.Conn.FieldList(table, fieldWildcard)
}
func (h ServerHandler) HandleStmtPrepare(query string) (int, int, interface{}, error) {
	stmt, err := h.Conn.Prepare(query)
	if err != nil {
		return 0, 0, nil, err
	}
	return stmt.ParamNum(), stmt.ColumnNum(), stmt, nil
}
func (h ServerHandler) HandleStmtExecute(context interface{}, query string, args []interface{}) (*mysql.Result, error) {
	return context.(*client.Stmt).Execute(args...)
}

func (h ServerHandler) HandleStmtClose(context interface{}) error {
	return context.(*client.Stmt).Close()
}

func (h ServerHandler) HandleOtherCommand(cmd byte, data []byte) error {
	return mysql.NewError(
		mysql.ER_UNKNOWN_ERROR,
		fmt.Sprintf("command %d is not supported now", cmd),
	)
}

func handleCon(c net.Conn) {
	cfg := Config{
		Host: "127.0.0.1",
		Port: 3306,
		User: "root",
	}
	sh, err := NewServerHandler(cfg)
	if err != nil {
		panic(err)
	}
	conn, err := server.NewConn(c, "root", "", sh)
	if err != nil {
		panic(err)
	}
	for {
		if err := conn.HandleCommand(); err != nil {
			if strings.Contains(err.Error(), "connection closed") {
				continue
			}
			panic(err)
		}
	}
}

func main() {
	l, err := net.Listen("tcp", "127.0.0.1:4000")
	if err != nil {
		panic(err)
	}
	for {
		c, err := l.Accept()
		if err != nil {
			panic(err)
		}
		go handleCon(c)
	}
}

Things that we might consider to change:

  1. Change the example to handle multiple connections.
  2. Add a note about handling multiple connections
  3. Expose the error for connection closed to make it easier to check. Or use an existing error like io.EOF if that makes sense.

@dveeden
Copy link
Collaborator Author

dveeden commented Feb 7, 2025

With this I was also able to run sysbench --mysql-host=127.0.0.1 --mysql-port=4000 --mysql-user=root --mysql-db=test oltp_read_only run --max-time=30 --threads=10

@matttm
Copy link

matttm commented Feb 7, 2025

Though I'm new to the underlying processes of mysql. i'm happy to try implementing one of these ways forward.

I assume with approach one, the work should be done in the server package?

@dveeden
Copy link
Collaborator Author

dveeden commented Feb 7, 2025

The example I posted in here works. The example in the readme doesn't as that only handles a single connection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants