go-mysql-server is a data-source agnostic SQL engine and server which runs queries on data sources you provide, using the MySQL dialect and wire protocol. A simple in-memory database implementation is included, and you can query any data source you want by implementing your own backend.
Dolt, a SQL database with Git-style versioning, is the main production database implementation of this package. Check out that project for a reference implementation. Or, hop into the Dolt Discord server here if you want to talk to the core developers behind go-mysql-server and Dolt.
With the exception of specific limitations (see below), go-mysql-server is a drop-in replacement for MySQL. Any client library, tool, query, SQL syntax, SQL function, etc. that works with MySQL should also work with go-mysql-server. If you find a gap in functionality, please file an issue.
For full MySQL compatibility documentation, see the Dolt docs on this topic.
go-mysql-server has two primary uses case:
Stand-in for MySQL in a golang test environment, using the built-in
memory
database implementation.
Providing access to arbitrary data sources with SQL queries by implementing a handful of interfaces. The most complete real-world implementation is Dolt.
Add go-mysql-server as a dependency to your project. In the
directory with the go.mod
file, run:
go get github.com/dolthub/go-mysql-server@latest
The in-memory test server can replace a real MySQL server in tests. Start the server using the code in the _example directory, also reproduced below.
package main
import (
"context"
"fmt"
"time"
"github.com/dolthub/vitess/go/vt/proto/query"
sqle "github.com/dolthub/go-mysql-server"
"github.com/dolthub/go-mysql-server/memory"
"github.com/dolthub/go-mysql-server/server"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/go-mysql-server/sql/types"
)
// This is an example of how to implement a MySQL server.
// After running the example, you may connect to it using the following:
//
// > mysql --host=localhost --port=3306 --user=root mydb --execute="SELECT * FROM mytable;"
// +----------+-------------------+-------------------------------+----------------------------+
// | name | email | phone_numbers | created_at |
// +----------+-------------------+-------------------------------+----------------------------+
// | Jane Deo | janedeo@gmail.com | ["556-565-566","777-777-777"] | 2022-11-01 12:00:00.000001 |
// | Jane Doe | jane@doe.com | [] | 2022-11-01 12:00:00.000001 |
// | John Doe | john@doe.com | ["555-555-555"] | 2022-11-01 12:00:00.000001 |
// | John Doe | johnalt@doe.com | [] | 2022-11-01 12:00:00.000001 |
// +----------+-------------------+-------------------------------+----------------------------+
//
// The included MySQL client is used in this example, however any MySQL-compatible client will work.
var (
dbName = "mydb"
tableName = "mytable"
address = "localhost"
port = 3306
)
func main() {
pro := createTestDatabase()
engine := sqle.NewDefault(pro)
session := memory.NewSession(sql.NewBaseSession(), pro)
ctx := sql.NewContext(context.Background(), sql.WithSession(session))
ctx.SetCurrentDatabase("test")
// This variable may be found in the "users_example.go" file. Please refer to that file for a walkthrough on how to
// set up the "mysql" database to allow user creation and user checking when establishing connections. This is set
// to false for this example, but feel free to play around with it and see how it works.
if enableUsers {
if err := enableUserAccounts(ctx, engine); err != nil {
panic(err)
}
}
config := server.Config{
Protocol: "tcp",
Address: fmt.Sprintf("%s:%d", address, port),
}
s, err := server.NewServer(config, engine, memory.NewSessionBuilder(pro), nil)
if err != nil {
panic(err)
}
if err = s.Start(); err != nil {
panic(err)
}
}
func createTestDatabase() *memory.DbProvider {
db := memory.NewDatabase(dbName)
db.BaseDatabase.EnablePrimaryKeyIndexes()
pro := memory.NewDBProvider(db)
session := memory.NewSession(sql.NewBaseSession(), pro)
ctx := sql.NewContext(context.Background(), sql.WithSession(session))
table := memory.NewTable(db, tableName, sql.NewPrimaryKeySchema(sql.Schema{
{Name: "name", Type: types.Text, Nullable: false, Source: tableName, PrimaryKey: true},
{Name: "email", Type: types.Text, Nullable: false, Source: tableName, PrimaryKey: true},
{Name: "phone_numbers", Type: types.JSON, Nullable: false, Source: tableName},
{Name: "created_at", Type: types.MustCreateDatetimeType(query.Type_DATETIME, 6), Nullable: false, Source: tableName},
}), db.GetForeignKeyCollection())
db.AddTable(tableName, table)
creationTime := time.Unix(0, 1667304000000001000).UTC()
_ = table.Insert(ctx, sql.NewRow("Jane Deo", "janedeo@gmail.com", types.MustJSON(`["556-565-566", "777-777-777"]`), creationTime))
_ = table.Insert(ctx, sql.NewRow("Jane Doe", "jane@doe.com", types.MustJSON(`[]`), creationTime))
_ = table.Insert(ctx, sql.NewRow("John Doe", "john@doe.com", types.MustJSON(`["555-555-555"]`), creationTime))
_ = table.Insert(ctx, sql.NewRow("John Doe", "johnalt@doe.com", types.MustJSON(`[]`), creationTime))
return pro
}
This example populates the database by creating memory.Database
and
memory.Table
objects via golang code, but you can also populate it
by issuing CREATE DATABASE
, CREATE TABLE
, etc. statements to the
server once it's running.
Once the server is running, connect with any MySQL client, including
the golang MySQL connector and the mysql
shell.
> mysql --host=localhost --port=3306 --user=root mydb --execute="SELECT * FROM mytable;"
+----------+-------------------+-------------------------------+----------------------------+
| name | email | phone_numbers | created_at |
+----------+-------------------+-------------------------------+----------------------------+
| Jane Deo | janedeo@gmail.com | ["556-565-566","777-777-777"] | 2022-11-01 12:00:00.000001 |
| Jane Doe | jane@doe.com | [] | 2022-11-01 12:00:00.000001 |
| John Doe | john@doe.com | ["555-555-555"] | 2022-11-01 12:00:00.000001 |
| John Doe | johnalt@doe.com | [] | 2022-11-01 12:00:00.000001 |
+----------+-------------------+-------------------------------+----------------------------+
The in-memory database implementation included with this package is intended for use in tests. It has specific limitations that we know of:
CREATE TABLE
, INSERT
, etc.) to a single goroutine.START TRANSACTION
, ROLLBACK
, and COMMIT
are no-ops.You can create your own backend to query your own data sources by implementing some interfaces. For detailed instructions, see the backend guide.
Are you building a database backend using go-mysql-server? We would like to hear from you and include you in this list.
go-mysql-server's security policy is maintained in this repository. Please follow the disclosure instructions there. Please do not initially report security issues in this repository's public GitHub issues.
go-mysql-server was originally developed by the {source-d}
organzation, and this repository was originally forked from
src-d. We want to thank
the entire {source-d}
development team for their work on this
project, especially Miguel Molina (@erizocosmico) and Juanjo Álvarez
Martinez (@juanjux).
Apache License 2.0, see LICENSE