tinygomysql

package
v1.2.4 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: Apache-2.0, MPL-2.0 Imports: 36 Imported by: 0

README

tinygomysql

tinygomysql is a fork of go-sql-driver/mysql v1.10.0, carried here because two things it relies on do not exist under TinyGo. It speaks MySQL and MariaDB over database/sql and registers the driver name mysql, exactly as upstream does.

Import the higher-level database/sql/mysql package unless a direct test of this backend is required. That package selects this fork on TinyGo and force_tinygo_logic builds, and the unmodified upstream driver everywhere else.

Licence

This directory is Mozilla Public License 2.0, not the Apache 2.0 of the rest of the repository. MPL-2.0 is a file-level copyleft: these files stay under it, combining them with Apache-2.0 code is fine, and modifications to them must be published under MPL-2.0. LICENSE and AUTHORS are upstream's own.

Every change to upstream source is marked with a PETITWEB: comment so a diff against v1.10.0 stays readable.

Divergences from upstream

Connection pooling

connCheck asks net.Conn for a syscall.Conn and calls SyscallConn. TinyGo's net.TCPConn implements the interface but returns an error, so every pooled connection was judged dead and re-dialed. Measured before the fix: 200 sequential SELECT 1 opened 201 server connections and took 190 ms; after, 0 and 34 ms, matching standard Go.

conncheck.go gains && !tinygo and conncheck_dummy.go gains tinygo ||. The constraint is on tinygo alone, not force_tinygo_logic: a force_tinygo_logic build runs on host Go, whose net.Conn supports SyscallConn normally.

TLS

TinyGo ships a stub crypto/tls whose Client() panics, so upstream's handshake could not run at all. MySQL upgrades in band — plaintext capability packet first, then TLS on the very same socket — which rules out any TLS API that owns its own dialing.

TLS therefore goes through the https.DialPlain / https.Upgrade seam, backed by the OS TLS stack: Secure Transport on macOS, mbedTLS on Linux, Schannel on Windows. crypto/tls is no longer imported anywhere in this package.

Consequences for callers:

  • Config.TLS is a *https.Config rather than a *tls.Config. It takes PEM bytes, the one representation every backend accepts.
  • RegisterTLSConfig takes a *https.Config. Prefer database/sql/mysql.RegisterTLSConfig, which has the same signature on both backends and converts for upstream.
  • A tls= DSN requires the driver's own dialer, because the handshake needs the socket descriptor and TinyGo's net.TCPConn will not surrender one. Setting DialFunc or RegisterDialContext alongside tls= returns https.ErrNotUpgradable instead of silently connecting in cleartext.
  • macOS caps at TLS 1.2 on this path; Apple never added 1.3 to Secure Transport. Build with -tags darwinstarttlswith13 to use mbedTLS and get 1.3.
  • Client certificates are unsupported on macOS, as elsewhere in https.

Known limitations under TinyGo

  • The cooperative scheduler breaks cancellation. Under -scheduler=tasks a blocking socket call holds the runtime, so the watcher goroutine that cancels a query never runs and the deadline is ignored with no error. The threads scheduler is the default on desktop targets.
  • Unix sockets are unavailable. TinyGo's net supports tcp only.
  • DSN timeout= has no effect. TinyGo's net.Dialer.DialContext ignores both Timeout and the context. readTimeout, writeTimeout and context deadlines on queries do work.
  • LocalAddr() is nil on a dialed connection, so log lines read read tcp <nil>->host:port.

Verified

TinyGo 0.41.1 on darwin/arm64 against MariaDB 11.8 and MySQL 8.4: all types, parseTime, prepared statements, interpolateParams, transactions, context cancellation, 16-goroutine concurrency, 4 MB LONGBLOB, multiStatements, compress=true, caching_sha2_password full authentication including RSA, and MariaDB ed25519. TLS verified against a MariaDB configured with a private CA: skip-verify, preferred, a registered custom CA, and rejection of both a mismatched hostname and an untrusted root.

Documentation

Overview

Package mysql provides a MySQL driver for Go's database/sql package.

The driver should be used via the database/sql package:

import "database/sql"
import _ "github.com/go-sql-driver/mysql"

db, err := sql.Open("mysql", "user:password@/dbname")

See https://github.com/go-sql-driver/mysql#usage for details

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidConn       = errors.New("invalid connection")
	ErrMalformPkt        = errors.New("malformed packet")
	ErrNoTLS             = errors.New("TLS requested but server does not support TLS")
	ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
	ErrNativePassword    = errors.New("this user requires mysql native password authentication")
	ErrOldPassword       = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
	ErrUnknownPlugin     = errors.New("this authentication plugin is not supported")
	ErrOldProtocol       = errors.New("MySQL server does not support required protocol 41+")
	ErrPktSync           = errors.New("commands out of sync. You can't run this command now")
	ErrPktSyncMul        = errors.New("commands out of sync. Did you run multiple statements at once?")
	ErrPktTooLarge       = errors.New("packet for query is too large. Try adjusting the `Config.MaxAllowedPacket`")
	ErrBusyBuffer        = errors.New("busy buffer")
)

Various errors the driver might return. Can change between driver versions.

Functions

func DeregisterDialContext

func DeregisterDialContext(net string)

DeregisterDialContext removes the custom dial function registered with the given net.

func DeregisterLocalFile

func DeregisterLocalFile(filePath string)

DeregisterLocalFile removes the given filepath from the allowlist.

func DeregisterReaderHandler

func DeregisterReaderHandler(name string)

DeregisterReaderHandler removes the ReaderHandler function with the given name from the registry.

func DeregisterServerPubKey

func DeregisterServerPubKey(name string)

DeregisterServerPubKey removes the public key registered with the given name.

func DeregisterTLSConfig

func DeregisterTLSConfig(key string)

DeregisterTLSConfig removes the tls.Config associated with key.

func NewConnector

func NewConnector(cfg *Config) (driver.Connector, error)

NewConnector returns new driver.Connector.

func RegisterDial deprecated

func RegisterDial(network string, dial DialFunc)

RegisterDial registers a custom dial function. It can then be used by the network address mynet(addr), where mynet is the registered new network. addr is passed as a parameter to the dial function.

Deprecated: users should call RegisterDialContext instead

func RegisterDialContext

func RegisterDialContext(net string, dial DialContextFunc)

RegisterDialContext registers a custom dial function. It can then be used by the network address mynet(addr), where mynet is the registered new network. The current context for the connection and its address is passed to the dial function.

func RegisterLocalFile

func RegisterLocalFile(filePath string)

RegisterLocalFile adds the given file to the file allowlist, so that it can be used by "LOAD DATA LOCAL INFILE <filepath>". Alternatively you can allow the use of all local files with the DSN parameter 'allowAllFiles=true'

filePath := "/home/gopher/data.csv"
mysql.RegisterLocalFile(filePath)
err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
if err != nil {
...

func RegisterReaderHandler

func RegisterReaderHandler(name string, handler func() io.Reader)

RegisterReaderHandler registers a handler function which is used to receive a io.Reader. The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>". If the handler returns a io.ReadCloser Close() is called when the request is finished.

mysql.RegisterReaderHandler("data", func() io.Reader {
	var csvReader io.Reader // Some Reader that returns CSV data
	... // Open Reader here
	return csvReader
})
err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
if err != nil {
...

func RegisterServerPubKey

func RegisterServerPubKey(name string, pubKey *rsa.PublicKey)

RegisterServerPubKey registers a server RSA public key which can be used to send data in a secure manner to the server without receiving the public key in a potentially insecure way from the server first. Registered keys can afterwards be used adding serverPubKey=<name> to the DSN.

Note: The provided rsa.PublicKey instance is exclusively owned by the driver after registering it and may not be modified.

data, err := os.ReadFile("mykey.pem")
if err != nil {
	log.Fatal(err)
}

block, _ := pem.Decode(data)
if block == nil || block.Type != "PUBLIC KEY" {
	log.Fatal("failed to decode PEM block containing public key")
}

pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
	log.Fatal(err)
}

if rsaPubKey, ok := pub.(*rsa.PublicKey); ok {
	mysql.RegisterServerPubKey("mykey", rsaPubKey)
} else {
	log.Fatal("not a RSA public key")
}

func RegisterTLSConfig

func RegisterTLSConfig(key string, config *https.Config) error

RegisterTLSConfig registers a custom TLS configuration to be used with sql.Open. Use the key as a value in the DSN where tls=value.

PETITWEB: the parameter is https.Config rather than crypto/tls.Config. TinyGo ships a stub crypto/tls whose Client() panics, so TLS goes through the OS stack instead; https.Config is that stack's backend-neutral configuration and takes PEM bytes, the one representation every backend accepts.

pem, err := os.ReadFile("/path/ca-cert.pem")
if err != nil {
    log.Fatal(err)
}
mysql.RegisterTLSConfig("custom", &https.Config{RootCAs: [][]byte{pem}})
db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")

func SetLogger

func SetLogger(logger Logger) error

SetLogger is used to set the default logger for critical errors. The initial logger is os.Stderr.

Types

type Config

type Config struct {
	User                 string            // Username
	Passwd               string            // Password (requires User)
	Net                  string            // Network (e.g. "tcp", "tcp6", "unix". default: "tcp")
	Addr                 string            // Address (default: "127.0.0.1:3306" for "tcp" and "/tmp/mysql.sock" for "unix")
	DBName               string            // Database name
	Params               map[string]string // Connection parameters
	ConnectionAttributes string            // Connection Attributes, comma-delimited string of user-defined "key:value" pairs
	Collation            string            // Connection collation. When set, this will be set in SET NAMES <charset> COLLATE <collation> query
	Loc                  *time.Location    // Location for time.Time values
	MaxAllowedPacket     int               // Max packet size allowed
	ServerPubKey         string            // Server public key name
	TLSConfig            string            // TLS configuration name
	TLS                  *https.Config     // TLS configuration, its priority is higher than TLSConfig
	Timeout              time.Duration     // Dial timeout
	ReadTimeout          time.Duration     // I/O read timeout
	WriteTimeout         time.Duration     // I/O write timeout
	Logger               Logger            // Logger
	// DialFunc specifies the dial function for creating connections
	DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)

	AllowAllFiles            bool // Allow all files to be used with LOAD DATA LOCAL INFILE
	AllowCleartextPasswords  bool // Allows the cleartext client side plugin
	AllowFallbackToPlaintext bool // Allows fallback to unencrypted connection if server does not support TLS
	AllowNativePasswords     bool // Allows the native password authentication method
	AllowOldPasswords        bool // Allows the old insecure password method
	CheckConnLiveness        bool // Check connections for liveness before using them
	ClientFoundRows          bool // Return number of matching rows instead of rows changed
	ColumnsWithAlias         bool // Prepend table alias to column names
	InterpolateParams        bool // Interpolate placeholders into query string
	MultiStatements          bool // Allow multiple statements in one query
	ParseTime                bool // Parse time values to time.Time
	RejectReadOnly           bool // Reject read-only connections
	// contains filtered or unexported fields
}

Config is a configuration parsed from a DSN string. If a new Config is created instead of being parsed from a DSN string, the NewConfig function should be used, which sets default values.

func NewConfig

func NewConfig() *Config

NewConfig creates a new Config and sets default values.

func ParseDSN

func ParseDSN(dsn string) (cfg *Config, err error)

ParseDSN parses the DSN string to a Config

func (*Config) Apply

func (c *Config) Apply(opts ...Option) error

Apply applies the given options to the Config object.

func (*Config) Clone

func (cfg *Config) Clone() *Config

func (*Config) FormatDSN

func (cfg *Config) FormatDSN() string

FormatDSN formats the given Config into a DSN string which can be passed to the driver.

Note: use NewConnector and database/sql.OpenDB to open a connection from a *Config.

type DialContextFunc

type DialContextFunc func(ctx context.Context, addr string) (net.Conn, error)

DialContextFunc is a function which can be used to establish the network connection. Custom dial functions must be registered with RegisterDialContext

type DialFunc deprecated

type DialFunc func(addr string) (net.Conn, error)

DialFunc is a function which can be used to establish the network connection. Custom dial functions must be registered with RegisterDial

Deprecated: users should register a DialContextFunc instead

type Logger

type Logger interface {
	Print(v ...any)
}

Logger is used to log critical error messages.

type MySQLDriver

type MySQLDriver struct{}

MySQLDriver is exported to make the driver directly accessible. In general the driver is used via the database/sql package.

func (MySQLDriver) Open

func (d MySQLDriver) Open(dsn string) (driver.Conn, error)

Open new Connection. See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how the DSN string is formatted

func (MySQLDriver) OpenConnector

func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error)

OpenConnector implements driver.DriverContext.

type MySQLError

type MySQLError struct {
	Number   uint16
	SQLState [5]byte
	Message  string
}

MySQLError is an error type which represents a single MySQL error

func (*MySQLError) Error

func (me *MySQLError) Error() string

func (*MySQLError) Is

func (me *MySQLError) Is(err error) bool

type NopLogger

type NopLogger struct{}

NopLogger is a nop implementation of the Logger interface.

func (*NopLogger) Print

func (nl *NopLogger) Print(_ ...any)

Print implements Logger interface.

type NullTime deprecated

type NullTime sql.NullTime

NullTime represents a time.Time that may be NULL. NullTime implements the Scanner interface so it can be used as a scan destination:

var nt NullTime
err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
...
if nt.Valid {
   // use nt.Time
} else {
   // NULL value
}

This NullTime implementation is not driver-specific

Deprecated: NullTime doesn't honor the loc DSN parameter. NullTime.Scan interprets a time as UTC, not the loc DSN parameter. Use sql.NullTime instead.

func (*NullTime) Scan

func (nt *NullTime) Scan(value any) (err error)

Scan implements the Scanner interface. The value type must be time.Time or string / []byte (formatted time-string), otherwise Scan fails.

func (NullTime) Value

func (nt NullTime) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type Option

type Option func(*Config) error

Functional Options Pattern https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis

func BeforeConnect

func BeforeConnect(fn func(context.Context, *Config) error) Option

BeforeConnect sets the function to be invoked before a connection is established.

func Charset

func Charset(charset, collation string) Option

Charset sets the connection charset and collation.

charset is the connection charset. collation is the connection collation. It can be null or empty string.

When collation is not specified, `SET NAMES <charset>` command is sent when the connection is established. When collation is specified, `SET NAMES <charset> COLLATE <collation>` command is sent when the connection is established.

func EnableCompression

func EnableCompression(yes bool) Option

EnableCompress sets the compression mode.

func TimeTruncate

func TimeTruncate(d time.Duration) Option

TimeTruncate sets the time duration to truncate time.Time values in query parameters.

type Result

type Result interface {
	driver.Result
	// AllRowsAffected returns a slice containing the affected rows for each
	// executed statement.
	AllRowsAffected() []int64
	// AllLastInsertIds returns a slice containing the last inserted ID for each
	// executed statement.
	AllLastInsertIds() []int64
}

Result exposes data not available through *connection.Result.

This is accessible by executing statements using sql.Conn.Raw() and downcasting the returned result:

res, err := rawConn.Exec(...)
res.(mysql.Result).AllRowsAffected()

Jump to

Keyboard shortcuts

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