Documentation
¶
Overview ¶
Package db manages MySQL connections, statement execution and cancellation.
Index ¶
- Constants
- func DSN(ds *config.DataSource, password, addr string) (string, error)
- func IsInterrupted(err error) bool
- func Probe(ctx context.Context, ds *config.DataSource, password string) (string, error)
- func ReachedServer(err error) bool
- type Conn
- func (c *Conn) Begin(ctx context.Context) error
- func (c *Conn) Close() error
- func (c *Conn) Commit(ctx context.Context) error
- func (c *Conn) DataSource() *config.DataSource
- func (c *Conn) Exec(ctx context.Context, sql string) (ExecResult, error)
- func (c *Conn) InTransaction() bool
- func (c *Conn) Owns(id uint64) bool
- func (c *Conn) Query(ctx context.Context, sql string, opt Options) *Stream
- func (c *Conn) Rollback(ctx context.Context) error
- func (c *Conn) ServerVersion() string
- func (c *Conn) WithControl(ctx context.Context, fn func(*sql.Conn) error) error
- type Event
- type EventKind
- type ExecResult
- type Options
- type Result
- type Stream
- func (s *Stream) Cancel() error
- func (s *Stream) Close() error
- func (s *Stream) ConnectionID() uint64
- func (s *Stream) Err() error
- func (s *Stream) Result() (Result, bool)
- func (s *Stream) Truncated() bool
- func (s *Stream) WaitConnectionID(ctx context.Context) (uint64, error)
- func (s *Stream) Warnings() []Warning
- type Warning
Constants ¶
const DialTimeout = 10 * time.Second
DialTimeout bounds how long a connection attempt waits before failing, so an unreachable host does not hang the UI.
const MaxQueryConns = 4
MaxQueryConns bounds the query pool. A handful is plenty: the user runs one statement at a time, and the spare slots exist so a stalled stream cannot starve the next one.
Variables ¶
This section is empty.
Functions ¶
func DSN ¶
func DSN(ds *config.DataSource, password, addr string) (string, error)
DSN builds a driver connection string for ds.
addr overrides the host:port the driver dials, which is how an SSH tunnel is wired in: the datasource still describes the remote database while the driver connects to the local listener.
The DSN is assembled through mysql.Config rather than string concatenation so that passwords containing "@", "/" or ":" are escaped correctly.
It fails rather than returning a usable-looking string when the TLS settings cannot be honoured: a connection that quietly verified less than was asked of it is the failure this whole path exists to prevent.
func IsInterrupted ¶
IsInterrupted reports whether err is the server saying the statement was killed. Callers use it to present a cancellation as an outcome the user chose rather than as a failure.
func Probe ¶
Probe opens a connection to ds and returns the server version.
sql.Open only validates the DSN, so it succeeds even against a host that is down. The version query is what actually proves reachability.
func ReachedServer ¶ added in v0.5.0
ReachedServer reports whether err is the server refusing, rather than the connection to it failing.
A numbered MySQL error is proof the statement got there: the server read it and answered. Anything else — a socket that has gone, a driver's bad connection — could have failed at any hop between here and the database, and is the only case a caller may attribute to something underneath, such as a bastion that has stopped forwarding.
Types ¶
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a live connection to one datasource.
It deliberately holds two separate connections to the same server:
- pool serves the statements the user runs
- control is reserved for KILL QUERY and catalog reads
MySQL will not accept another statement on a connection until the current result set has been read to the end, so without this split a long stream would block both cancellation and schema browsing — the two things most needed while a long stream is in flight.
func Open ¶
Open connects to ds. addr overrides the dialled address, which is how an SSH tunnel is wired in.
func (*Conn) Begin ¶ added in v0.3.0
Begin opens a transaction and pins the connection it runs on.
The pin is the whole point. Statements ordinarily take a connection from the pool and hand it straight back, so a transaction opened on one would be abandoned by the next statement — BEGIN would have nothing to commit and ROLLBACK nothing to undo, while both reported success.
The connection id is read once, here, rather than per statement: it is the same connection every time, and KILL QUERY still has something to aim at.
func (*Conn) DataSource ¶
func (c *Conn) DataSource() *config.DataSource
DataSource returns the datasource this connection serves.
func (*Conn) InTransaction ¶ added in v0.3.0
InTransaction reports whether a transaction is open on this connection.
func (*Conn) Owns ¶ added in v0.5.0
Owns reports whether a server-side connection id belongs to this session.
func (*Conn) Query ¶
Query runs sql and streams the result.
It returns immediately; connecting, sending the statement and reading rows all happen in the background. Failures — including a syntax error — surface through Err once Events is closed.
func (*Conn) ServerVersion ¶
ServerVersion returns the version string reported at connection time.
func (*Conn) WithControl ¶
WithControl runs fn with exclusive use of the control connection.
This connection is reserved for cancellation and catalog reads, which is what keeps schema browsing responsive while the query pool streams a large result. Access goes through a callback rather than an accessor so that concurrent use — which would corrupt the protocol and kill the connection for good — cannot be expressed.
fn must not retain the connection beyond its return.
type Event ¶
type Event struct {
Kind EventKind
// Columns and Types are set on EventColumns.
Columns []string
Types []*sql.ColumnType
// Rows is set on EventRows.
Rows [][]any
}
Event is one update from a running statement.
There is deliberately no terminal event: the stream ends when Events is closed, and the reason is read from Err. A final event could be dropped when a cancelled stream closes, leaving the caller unable to tell a cancellation from a clean finish.
type ExecResult ¶
ExecResult summarises a statement that returns no rows.
type Options ¶
type Options struct {
// ChunkSize is how many rows are gathered before a batch is published.
ChunkSize int
// MaxRows caps how many rows are read. Zero means no cap.
MaxRows int
// Schema is the one an unqualified name resolves against. Empty means the
// schema the connection was opened with.
Schema string
// Exec sends the statement as one that returns a count rather than rows,
// which is the only way the server's affected-row count can be read.
//
// It is the caller's decision because only the caller has the parsed
// statement; asking the driver afterwards is not possible, since a write
// sent as a query simply yields a result set with no columns and the
// count is gone.
Exec bool
}
Options tunes how a result set is streamed.
type Result ¶ added in v0.2.0
Result is what a statement that returned no rows did instead.
RowsAffected is MySQL's own count, which for an UPDATE is the number of rows *changed* rather than matched: an UPDATE setting a column to the value it already held reports zero, and that is the server's answer rather than a miscount.
type Stream ¶
type Stream struct {
// Events yields progress until it is closed.
Events <-chan Event
// contains filtered or unexported fields
}
Stream is a statement in flight.
Everything after Query returns is delivered on Events, which is closed when the statement ends for any reason. This shape exists because the driver's QueryContext blocks until the server produces a result header: a slow statement would otherwise freeze the caller — and in the TUI, the caller is the event loop that has to stay responsive enough to cancel it.
func (*Stream) Cancel ¶
Cancel stops the statement on the server.
Cancelling the context alone only detaches the client: the server keeps executing until it finishes. KILL QUERY, sent over the separate control connection, is what actually stops the work.
func (*Stream) ConnectionID ¶
ConnectionID returns the server-side connection id, or zero if the statement has not reached the server yet.
func (*Stream) Err ¶
Err reports why the stream ended, once Events is closed. It is nil when the result set was read to the end.
This mirrors sql.Rows and bufio.Scanner: iterate until the source is exhausted, then ask why it stopped.
func (*Stream) Result ¶ added in v0.2.0
Result reports what a write did, and whether there was one to report.
Like Err, it is read after Events closes rather than delivered as an event, for the reason given on Event: a terminal event can be dropped when a cancelled stream closes, and the caller would not be able to tell the difference.
func (*Stream) Truncated ¶
Truncated reports whether the stream stopped at Options.MaxRows rather than at the end of the result set.
func (*Stream) WaitConnectionID ¶
WaitConnectionID blocks until the server-side connection id is known.
func (*Stream) Warnings ¶ added in v0.2.0
Warnings lists what the server said about the statement, once Events has closed.
They are read on the connection that ran the statement, before it goes back to the pool. SHOW WARNINGS reports on the last statement executed on that connection, so asking any later — or on any other connection — would answer about something else entirely.