sqle

package module
v0.14.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 21, 2023 License: Apache-2.0 Imports: 18 Imported by: 0

README

go-mysql-server

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 reference a implementation. Or, hop into the Dolt discord here if you want to talk to the core developers behind go-mysql-server and Dolt.

Compatibility

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.

Scope of this project

  • SQL server and engine to query your data sources.
  • In-memory database backend implementation suitable for use in tests.
  • Interfaces you can use to implement new backends to query your own data sources.
  • With a few caveats and using a full database implementation, a drop-in MySQL database replacement.

go-mysql-server has two primary uses case:

  1. Stand-in for MySQL in a golang test environment, using the built-in memory database implementation.

  2. Providing access to aribtrary data sources with SQL queries by implementing a handful of interfaces. The most complete real-world implementation is Dolt.

Installation

Add go-mysql-server as a dependency to your project. In the directory with the go.mod file, run:

go get github.com/gabereiser/go-mysql-server@latest

Using the in-memory test server

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.

var (
	dbName    = "mydb"
	tableName = "mytable"
	address   = "localhost"
	port      = 3306
)

func main() {
	ctx := sql.NewEmptyContext()
	engine := sqle.NewDefault(
		memory.NewDBProvider(
			createTestDatabase(ctx),
		))
	
	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.NewDefaultServer(config, engine)
	if err != nil {
		panic(err)
	}
	
    if err = s.Start(); err != nil {
		panic(err)
	}
}

func createTestDatabase(ctx *sql.Context) *memory.Database {
	db := memory.NewDatabase(dbName)
	db.EnablePrimaryKeyIndexes()
	table := memory.NewTable(tableName, sql.NewPrimaryKeySchema(sql.Schema{
		{Name: "name", Type: sql.Text, Nullable: false, Source: tableName, PrimaryKey: true},
		{Name: "email", Type: sql.Text, Nullable: false, Source: tableName, PrimaryKey: true},
		{Name: "phone_numbers", Type: sql.JSON, Nullable: false, Source: tableName},
		{Name: "created_at", Type: sql.Datetime, 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", sql.MustJSON(`["556-565-566", "777-777-777"]`), creationTime))
	_ = table.Insert(ctx, sql.NewRow("Jane Doe", "jane@doe.com", sql.MustJSON(`[]`), creationTime))
	_ = table.Insert(ctx, sql.NewRow("John Doe", "john@doe.com", sql.MustJSON(`["555-555-555"]`), creationTime))
	_ = table.Insert(ctx, sql.NewRow("John Doe", "johnalt@doe.com", sql.MustJSON(`[]`), creationTime))
	return db
}

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 |
+----------+-------------------+-------------------------------+----------------------------+

Limitations of the in-memory database implementation

The in-memory database implementation included with this package is intended for use in tests. It has specific limitations that we know of:

Custom backend implementations

You can create your own backend to query your own data sources by implementing some interfaces. For detailed instructions, see the backend guide.

Technical documentation for contributors and backend developers

  • Architecture is an overview of the various packages of the project and how they fit together.
  • Contribution guide for new contributors, including instructions for how to get your PR merged.

Powered by go-mysql-server

Are you building a database backend using go-mysql-server? We would like to hear from you and include you in this list.

Acknowledgements

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).

License

Apache License 2.0, see LICENSE

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ColumnsFromCheckDefinition

func ColumnsFromCheckDefinition(ctx *sql.Context, def *sql.CheckDefinition) ([]string, error)

ColumnsFromCheckDefinition retrieves the Column Names referenced by a CheckDefinition

func ResolveDefaults

func ResolveDefaults(tableName string, schema []*ColumnWithRawDefault) (sql.Schema, error)

ResolveDefaults takes in a schema, along with each column's default value in a string form, and returns the schema with the default values parsed and resolved.

Types

type ColumnWithRawDefault

type ColumnWithRawDefault struct {
	SqlColumn *sql.Column
	Default   string
}

type Config

type Config struct {
	// VersionPostfix to display with the `VERSION()` UDF.
	VersionPostfix string
	// IsReadOnly sets the engine to disallow modification queries.
	IsReadOnly     bool
	IsServerLocked bool
	// IncludeRootAccount adds the root account (with no password) to the list of accounts, and also enables
	// authentication.
	IncludeRootAccount bool
	// TemporaryUsers adds any users that should be included when the engine is created. By default, authentication is
	// disabled, and including any users here will enable authentication. All users in this list will have full access.
	// This field is only temporary, and will be removed as development on users and authentication continues.
	TemporaryUsers []TemporaryUser
}

Config for the Engine.

type Engine

type Engine struct {
	Analyzer          *analyzer.Analyzer
	LS                *sql.LockSubsystem
	ProcessList       sql.ProcessList
	MemoryManager     *sql.MemoryManager
	BackgroundThreads *sql.BackgroundThreads
	IsReadOnly        bool
	IsServerLocked    bool
	PreparedDataCache *PreparedDataCache
	// contains filtered or unexported fields
}

Engine is a SQL engine.

func New

func New(a *analyzer.Analyzer, cfg *Config) *Engine

New creates a new Engine with custom configuration. To create an Engine with the default settings use `NewDefault`. Should call Engine.Close() to finalize dependency lifecycles.

func NewDefault

func NewDefault(pro sql.DatabaseProvider) *Engine

NewDefault creates a new default Engine.

func (*Engine) AnalyzeQuery

func (e *Engine) AnalyzeQuery(
	ctx *sql.Context,
	query string,
) (sql.Node, error)

AnalyzeQuery analyzes a query and returns its sql.Node

func (*Engine) Close

func (e *Engine) Close() error

func (*Engine) CloseSession

func (e *Engine) CloseSession(connID uint32)

CloseSession deletes session specific prepared statement data

func (*Engine) PrepareQuery

func (e *Engine) PrepareQuery(
	ctx *sql.Context,
	query string,
) (sql.Node, error)

PrepareQuery returns a partially analyzed query

func (*Engine) Query

func (e *Engine) Query(ctx *sql.Context, query string) (sql.Schema, sql.RowIter, error)

Query executes a query.

func (*Engine) QueryNodeWithBindings

func (e *Engine) QueryNodeWithBindings(
	ctx *sql.Context,
	query string,
	parsed sql.Node,
	bindings map[string]sql.Expression,
) (sql.Schema, sql.RowIter, error)

QueryNodeWithBindings executes the query given with the bindings provided. If parsed is non-nil, it will be used instead of parsing the query from text.

func (*Engine) QueryWithBindings

func (e *Engine) QueryWithBindings(ctx *sql.Context, query string, bindings map[string]sql.Expression) (sql.Schema, sql.RowIter, error)

QueryWithBindings executes the query given with the bindings provided

func (*Engine) WithBackgroundThreads

func (e *Engine) WithBackgroundThreads(b *sql.BackgroundThreads) *Engine

type PreparedDataCache

type PreparedDataCache struct {
	// contains filtered or unexported fields
}

PreparedDataCache manages all the prepared data for every session for every query for an engine

func NewPreparedDataCache

func NewPreparedDataCache() *PreparedDataCache

func (*PreparedDataCache) CacheStmt

func (p *PreparedDataCache) CacheStmt(sessId uint32, query string, node sql.Node)

CacheStmt saves the prepared node and associates a ctx.SessionId and query to it

func (*PreparedDataCache) DeleteSessionData

func (p *PreparedDataCache) DeleteSessionData(sessId uint32)

DeleteSessionData clears a session along with all prepared queries for that session

func (*PreparedDataCache) GetCachedStmt

func (p *PreparedDataCache) GetCachedStmt(sessId uint32, query string) (sql.Node, bool)

GetCachedStmt will retrieve the prepared sql.Node associated with the ctx.SessionId and query if it exists it will return nil, false if the query does not exist

func (*PreparedDataCache) GetSessionData

func (p *PreparedDataCache) GetSessionData(sessId uint32) map[string]sql.Node

GetSessionData returns all the prepared queries for a particular session

func (*PreparedDataCache) UncacheStmt

func (p *PreparedDataCache) UncacheStmt(sessId uint32, query string)

UncacheStmt removes the prepared node associated with a ctx.SessionId and query to it

type ProcessList

type ProcessList struct {
	// contains filtered or unexported fields
}

ProcessList is a structure that keeps track of all the processes and their status.

func NewProcessList

func NewProcessList() *ProcessList

NewProcessList creates a new process list.

func (*ProcessList) AddConnection

func (pl *ProcessList) AddConnection(id uint32, addr string)

func (*ProcessList) AddPartitionProgress

func (pl *ProcessList) AddPartitionProgress(pid uint64, tableName, partitionName string, total int64)

AddPartitionProgress adds a new item to track progress from to the process with the given pid. If the pid or the table does not exist, it will do nothing.

func (*ProcessList) AddTableProgress

func (pl *ProcessList) AddTableProgress(pid uint64, name string, total int64)

AddTableProgress adds a new item to track progress from to the process with the given pid. If the pid does not exist, it will do nothing.

func (*ProcessList) BeginQuery

func (pl *ProcessList) BeginQuery(
	ctx *sql.Context,
	query string,
) (*sql.Context, error)

func (*ProcessList) ConnectionReady

func (pl *ProcessList) ConnectionReady(sess sql.Session)

func (*ProcessList) EndQuery

func (pl *ProcessList) EndQuery(ctx *sql.Context)

func (*ProcessList) Kill

func (pl *ProcessList) Kill(connID uint32)

Kill terminates all queries for a given connection id.

func (*ProcessList) Processes

func (pl *ProcessList) Processes() []sql.Process

Processes returns the list of current running processes.

func (*ProcessList) RemoveConnection

func (pl *ProcessList) RemoveConnection(connID uint32)

func (*ProcessList) RemovePartitionProgress

func (pl *ProcessList) RemovePartitionProgress(pid uint64, tableName, partitionName string)

RemovePartitionProgress removes an existing item tracking progress from the process with the given pid, if it exists.

func (*ProcessList) RemoveTableProgress

func (pl *ProcessList) RemoveTableProgress(pid uint64, name string)

RemoveTableProgress removes an existing item tracking progress from the process with the given pid, if it exists.

func (*ProcessList) UpdatePartitionProgress

func (pl *ProcessList) UpdatePartitionProgress(pid uint64, tableName, partitionName string, delta int64)

UpdatePartitionProgress updates the progress of the table partition with the given name for the process with the given pid.

func (*ProcessList) UpdateTableProgress

func (pl *ProcessList) UpdateTableProgress(pid uint64, name string, delta int64)

UpdateTableProgress updates the progress of the table with the given name for the process with the given pid.

type TemporaryUser

type TemporaryUser struct {
	Username string
	Password string
}

TemporaryUser is a user that will be added to the engine. This is for temporary use while the remaining features are implemented. Replaces the old "auth.New..." functions for adding a user.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL