Help us shape the future of Go! Take the 2026 Go Developer Survey

connedit

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package connedit adds, tests, edits and removes the connections a user creates from inside dbc — dbc web's connection form and the TUI's — as opposed to the config file's, which are the file's to change.

It is the UI-free half of both forms. The web turns its answers into HTTP statuses (web/conns.go) and the TUI into lines in the form and the log (tui/connform.go); what counts as a valid form, how a DSN is built from fields, what is kept when an edit leaves a field empty, and the order in which the running config and the saved-connections file are changed live here once, so the two UIs cannot drift apart on any of it.

It sits above both config and db (config must not import db: db opens what config describes), which is why it is a package of its own rather than part of either.

Where a connection lives:

                 ┌── config file ([[connection]]) ──┐
startup:  cfg ◄──┤                                  ├── file's names win a clash
(any dbc)        └── connections.toml ──────────────┘
                        ▲
                        └── Editor.Add / Edit / Delete (dbc web, the TUI)

Every change goes config first, file second:

Add ──► cfg.AddConn ──── name taken? ──► Conflict
         │
         └► saved.Add ── fails? ───────► cfg.RemoveConn (undo), error

Edit ─► web-added? ─ no ─► Invalid ("edit the file")
         │ FormDSN, Check, expand
         │ reconnects (name, driver, DSN or TLS changed)?
         │     └► inUse(name) refuses? ─► that refusal
         ├► cfg.ReplaceConn ─ clash / gone ─► Conflict / NotFound
         ├► saved.Update ── fails? ─► cfg.ReplaceConn back, error
         └► reconnects? mgr.Drop(old name)

Delete ► web-added? ─ no ─► Invalid
         inUse(name) refuses? ─► that refusal
         cfg.RemoveConn, mgr.Drop, saved.Delete (a failure here is
         reported, but the removal stands for this run)

The config is changed first because it is where a duplicate name is caught atomically — two windows (or a window and a TUI) saving one name race there, under the config's lock, not in the file. A file that then fails to write is undone in the config, so the two never disagree about what exists for longer than one call.

"In use" is the caller's to judge: dbc web counts the query tabs on a connection across every window, the TUI has one — its active connection. Either way a connection something is on cannot be renamed, given a new DSN, driver or TLS, or removed, since that would pull the pool out from under a session and whatever transaction it holds. Its ai_rows alone can change at any time: the assistant reads it afresh for every question.

Index

Constants

View Source
const MaxName = 64

MaxName bounds a connection name. It is shown in the sidebar, the topbar and every history row: a name that long is a paste gone wrong.

View Source
const TestTimeoutCap = 30 * time.Second

TestTimeoutCap bounds a test when connect_timeout is 0 ("no limit"): a real connect may then wait on the OS, but a button press should answer while the user is still looking at it.

Variables

This section is empty.

Functions

This section is empty.

Types

type EditResult

type EditResult struct {
	Renamed    bool
	Reconnects bool // name, driver, DSN or TLS changed: the old pool was dropped
	Warnings   []string
}

EditResult says what an edit changed, for the caller's follow-ups: a rename moves what the UI keeps under the old name (saved tabs, schema picks, tab groups).

type Editor

type Editor struct {
	Cfg   *config.Config
	Saved *config.SavedStore
	// Mgr's pool for a connection is dropped when the connection changes
	// what it dials or goes away, so the next connect opens a fresh one
	// from the new entry. Nil drops nothing (tests).
	Mgr *db.Manager
	// App names this program in messages: "dbc web", "dbc". A clash in the
	// file means another such program added the name since this one read
	// it ("added by another dbc web").
	App string
}

Editor changes the connections a user added, in the running config and in the saved-connections file together.

func (*Editor) Add

func (e *Editor) Add(f Form) (warns []string, err error)

Add adds a connection to the running config and keeps it in the saved-connections file. It does not connect: each UI switches to it right after, where the connect's outcome shows the way every connect's does.

func (*Editor) Delete

func (e *Editor) Delete(name string, inUse func(name string) error) (removed bool, err error)

Delete forgets a connection added from inside dbc. The config file's are refused, as is one inUse (nil: nothing ever is) says something is on.

removed reports whether the running config lost it: a file that then fails to write is an error, but the connection stays gone for this run — putting it back would only hand the user a connection they just asked to be rid of — and comes back at the next start, which the error says.

The in-use check and the removal are not one atomic step: something that picks the connection in between gets a connect that fails ("unknown connection"), or a pool the Drop then closes. Either shows as an error there and breaks nothing else.

func (*Editor) Edit

func (e *Editor) Edit(name string, f Form, inUse func(name string) error) (EditResult, error)

Edit changes a connection added from inside dbc — its name, driver, DSN, TLS or ai_rows — in place. An empty DSN keeps the stored one (FormDSN).

Anything but ai_rows changes what the connection connects to, or what it is called, and whatever is on it holds a session opened under the old name and DSN; inUse (nil: nothing ever is) is asked then, and its error refuses the edit. An ai_rows change alone goes through regardless.

func (*Editor) FileRefusal

func (e *Editor) FileRefusal(name, verb string) error

FileRefusal is the answer to changing or removing a connection that is not dbc's to change: the config file's, or a built-in demo. verb is what was asked ("change", "remove").

func (*Editor) FormDSN

func (e *Editor) FormDSN(f *Form, from string) error

FormDSN settles f.DSN, whichever way the form gave it: built from fields, or kept from the stored connection from ("" when adding).

func (*Editor) Test

func (e *Editor) Test(ctx context.Context, f Form, from string) (TestResult, error)

Test opens the form's DSN, pings it within connect_timeout (capped at TestTimeoutCap when that is "no limit") and closes it again: nothing is saved, and no pool is left behind for a connection that may never be added. from is the connection being edited ("" when adding), whose stored DSN an empty one means (see FormDSN). The error is the form's — one that could never work; a connect that fails is a TestResult with ProbeErr set.

func (*Editor) UnsavedWarning

func (e *Editor) UnsavedWarning(what string) string

UnsavedWarning is the note for a connection change that no file keeps — there is no home directory to keep one in: it holds only until this program stops. what is the thing that lasts that long.

type Error

type Error struct {
	Kind Kind
	Msg  string
}

Error is a refusal the user can act on. Its message is written for the user, not the log. Any other error from this package is a failure to read or write the saved-connections file (500 in the web).

func (*Error) Error

func (e *Error) Error() string

type Form

type Form struct {
	Name   string `json:"name"`
	Driver string `json:"driver"`
	DSN    string `json:"dsn"`
	AIRows bool   `json:"ai_rows"`
	// TLS as typed: ${VAR}s unexpanded and a relative path relative, as the
	// saved file keeps them (config.SavedConn); TLSFor resolves them.
	config.TLSOpts

	// Parts, when set, is the DSN as fields; it replaces DSN (see FormDSN).
	Parts *db.DSNParts `json:"parts,omitempty"`
	// KeepPassword: editing in fields, the password field was left empty
	// over a stored password the form was not given — keep that one.
	KeepPassword bool `json:"keep_password,omitempty"`
}

Form is the add- or edit-connection form, as either UI collects it.

func (*Form) Check

func (f *Form) Check(needName bool) error

Check trims the form and turns away what could never work. needName: a test may be run before a name is typed; a save may not.

func (*Form) TLSFor

func (f *Form) TLSFor(name string) (config.TLSOpts, []string)

TLSFor is f's TLS settings as a connect uses them: paths resolved the way config.MergeSaved resolves a saved entry's, so what is tested, what runs now and what the next start merges are the same files.

type Kind

type Kind int

Kind says what sort of refusal an Error is, which each UI maps to its own words — dbc web to an HTTP status.

const (
	// Invalid is a form that could never work as given (400 in the web).
	Invalid Kind = iota + 1
	// NotFound is a connection that is not there (404).
	NotFound
	// Conflict is a change that raced another, or a name already taken (409).
	Conflict
)

type TestResult

type TestResult struct {
	OK       bool
	Took     time.Duration // open + ping, when it connected
	Note     string        // a qualified yes ("the file does not exist yet …")
	ProbeErr error         // the driver's refusal; nil when it connected
	Warnings []string      // unset ${VAR}s and the like
}

TestResult is the answer to "does this connect?". A connect that fails is not an error of the test — the test worked, and this is its answer — so it is ProbeErr here, not Test's error.

func (TestResult) ProbeMsg

func (r TestResult) ProbeMsg() string

ProbeMsg is the words for a failed test's ProbeErr.

Jump to

Keyboard shortcuts

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