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

workspace

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package workspace is one person's working state against the databases, and the rules for changing it — with no UI in it.

A Workspace holds the active connection and its catalog, the session pinned to it, the run in flight, and the last statement, error, result and plan. The TUI (package tui) and the browser UI (dbc web) are both clients: they turn keys and clicks into calls here, and draw what comes back. The rules therefore exist once — ONE RUN AT A TIME, A PINNED SESSION per active connection, CANCEL REACHES THE SERVER (see run.go) — rather than as two copies that drift.

Starting work, and where its outcome lands

Methods that start work (Connect, Run, Explain, RunScript) never block. They do the bookkeeping that must happen at once — claim the run slot, record history, bump a generation — and return a Start holding a Job: the blocking half, for the caller to run wherever its UI runs blocking work.

UI goroutine                         worker goroutine
────────────                         ────────────────
st, err := w.Run(...)  ── claims the slot, returns st.Job
  err != nil → refused (busy, no connection, nothing to run)
  log st.Notes
  hand st.Job to a worker ─────────► ev := st.Job()
                                        runs the statements
                                        LANDS the outcome under w.mu:
                                          frees the slot, sets last*
                                     ◄── returns ev (*RunDone)
draw ev (grid, status, log)

The TUI wraps a Job in a tea.Cmd, and the event comes back to Update as a message; the web layer runs it on a goroutine and writes the event to the workspace's SSE stream. The Job lands its own outcome before returning, so the workspace's state is right no matter when — or whether — a UI gets round to drawing the event.

Why a Job and not a channel of events: Bubble Tea already is an event loop with its own way of running blocking work (tea.Cmd), and the TUI's test harness drives it synchronously — press a key, run the command, feed its message back — so a workspace that ran its own goroutines would make every TUI test a race against a channel. Handing the blocking half back to the caller keeps both UIs in charge of their own concurrency.

Events that happen mid-run rather than at the end — a script's s.Show and s.Print — cannot be a Job's return value, and go to Options.Sink instead.

Locking

mu guards the bookkeeping and is only ever held briefly. sessMu guards the pinned session and is held for as long as a statement runs on it — which may be minutes. The rule that keeps them from deadlocking: never take sessMu while holding mu. (Taking mu under sessMu is not needed today and is avoided too.)

Index

Constants

View Source
const CompletionTimeout = DiagramTimeout

CompletionTimeout bounds reading a schema for completion. The ERD's bound: the same three catalog queries.

View Source
const DiagramTimeout = 30 * time.Second

DiagramTimeout bounds reading a schema for a diagram: three catalog queries, which take milliseconds on a normal database and seconds on a catalog of tens of thousands of tables. Longer than SchemaLookupTimeout, because here the user asked for the diagram and is waiting for it, while the assistant's lookup is a side errand that must not hold up a question.

View Source
const MaxSchemaTables = 8

MaxSchemaTables caps how many tables' columns go with one question. A query joining more than this is rare; a question whose words happen to name a dozen tables is not asking about all of them.

View Source
const SchemaLookupTimeout = 3 * time.Second

SchemaLookupTimeout bounds the catalog query a question's tables cost at send time. Past it the question goes without columns rather than waiting on a busy database: the model answering a little less precisely beats the user watching a spinner for a lookup they never asked for.

Variables

This section is empty.

Functions

func AttachColumns

func AttachColumns(ctx ai.Context, cols [][]db.Column, err error) (ai.Context, string)

AttachColumns puts a lookup's columns into the context a question was asked with. Only the tables the catalog could describe are kept, so the transcript's "sent: schema of …" names exactly the tables whose columns went. A failed lookup drops the schema entirely and returns the line the transcript shows about it: the question still goes, and the user is told why the model may be guessing column names rather than reading dbc's.

Both UIs call this, so the rule for what a failed or partial lookup sends cannot differ between the terminal and the browser.

func Pick

func Pick(ed Editor) (stmts []string, tag string)

Pick is what Ctrl+R executes: the selection's statements when there is a selection, otherwise the statement under the caret. tag names the run for the status bar and log: "query" when the buffer holds one statement, "statement 2/4" when the caret picked one of several, "selection" or "selection (3 statements)".

func PickAll

func PickAll(text string) (stmts []string, tag string)

PickAll is what Ctrl+Shift+R executes: every statement in the buffer, whatever the caret or selection says. A one-statement buffer is tagged "query", as Pick tags it, since "all" of one says nothing more.

func PlanStatus

func PlanStatus(p *explain.Plan) string

PlanStatus is the status-bar summary of a plan: "plan · 12 steps, cost 431 · 2 findings".

func Preview

func Preview(stmt string) string

Preview is a one-line, length-capped echo of a statement for the log.

func Project

func Project(src *model.Result, rows, cols []int) *model.Result

Project builds a Result holding the given result rows (in the order given — a slice of a display order) and result columns (likewise), with Raw carried along — so a copied selection exports exactly like a whole result: NULLs stay NULLs, numbers stay right-aligned in HTML.

Only the listed columns are taken, so a column the grid hides is left out by not listing it: a copy takes what the user sees. That is what makes hiding useful for sharing — hide the noisy columns, then copy the table for Teams.

func ResultStatus

func ResultStatus(r *model.Result, maxRows, shown int) string

ResultStatus is the status-bar summary of a result. shown is how many rows the UI displays (max_display_rows can cap it below len(r.Rows)).

func SortRows

func SortRows(order []int, r *model.Result, col int, desc bool, numeric []bool)

SortRows fills order with the display order of r's rows sorted by result column col (ascending, or descending with desc). col < 0 means the result's own order. numeric says which columns compare as numbers — pass export.NumericColumns(r); it is a parameter because a UI computes it once per result, and this may run on every header click.

NULLs sort LAST in both directions: "biggest first" should not open on a screen of nothing. A numeric column compares numbers, so 10 sorts after 9; anything else compares its text case-insensitively. The sort is stable, so rows that tie keep the result's order.

func StmtRange

func StmtRange(text string, caret int) [2]int

StmtRange is the byte range of the statement under the caret, for an editor's gutter marker — zero when the buffer holds fewer than two statements, since marking the only one says nothing.

func WidestNumeric

func WidestNumeric(r *model.Result, c int) int

WidestNumeric is the widest display value of numeric column c over EVERY row of r — for a column export.NumericColumns vouched for.

Auto-sizing measures only a grid's first few hundred rows (a text column's width costs a grapheme walk per value, and a huge result should not pay that on arrival), which is fine for text, whose width varies without pattern, and wrong for numbers, which grow: an id column sized by rows 1–500 shows "10…" at row 1,000. A number's display text is plain ASCII, so its width is len() — no decoding — and scanning every row is a length read per cell. NULLs count as their "NULL" text, which is how both grids draw them.

The widest text is measured rather than derived from the column's min/max: a float's text is not monotonic in its value (0.123456789 is wider than 1000), and a length scan is as cheap as a comparison.

Types

type Connected

type Connected struct {
	Name    string
	Catalog *model.Result // the connection's tables; nil if the catalog query failed
	// Databases and Schemas are the levels above the tables: the server's
	// databases, on a driver with a database picker (db.HasDatabases:
	// Postgres, MySQL), and this database's schemas with their table
	// counts, on one whose sidebar is loaded a schema at a time
	// (db.Navigable: Postgres). Schema is the one whose tables Catalog
	// holds, "" for every schema's. Each is empty on the other drivers,
	// and Databases or Schemas is nil when its listing failed.
	Databases []db.DatabaseInfo
	Schemas   []db.SchemaInfo
	Schema    string
	Err       error // the connect's failure (db.ErrCanceled when stopped); nil on success
	Changed   bool  // the active connection moved to Name
	// Left is the connection Changed moved away from, "" when there was
	// none (the first connect, or one after a Disconnect). A UI that knows
	// no other workspace is on it may close its pool once Release has run
	// — see Workspace.Derived for the one kind worth closing.
	Left string
	// Stale reports a connect superseded by a newer one: nothing landed,
	// and a UI draws nothing.
	Stale  bool
	Notes  []Note
	Status string // for a status bar; "" leaves it
	// Release, when non-nil, closes the session pinned to the connection
	// just left — rolling back what it left open. It is a separate Job
	// because it waits for any statement still running on that session,
	// and the switch itself must not.
	Release Job
	// Counts, when non-nil, fetches the row counts of the catalog's tables
	// for the sidebar; its event is a *RowCounts. It is separate so the
	// tables list draws at once and the numbers fill in after — a count
	// reads a table's rows, and the list must never wait for that.
	Counts Job
}

Connected lands a connect.

type Editor

type Editor struct {
	Text      string
	Caret     int
	Selection string
}

Editor is what a UI's SQL editor holds at the moment of a request: the buffer, the caret as a byte offset into it, and the selected text ("" when nothing is selected). The UI sends the caret and the workspace picks the statement, so "the statement under the caret" means the same thing in every UI.

type Event

type Event interface {
	// contains filtered or unexported methods
}

Event is something that happened in the workspace. The concrete types are the pointers below.

type ExplainDone

type ExplainDone struct {
	Tag     string
	Conn    string
	Stmt    string
	Plan    *explain.Plan
	Err     error
	Elapsed time.Duration
	Stale   bool
	Notes   []Note
	Status  string // as RunDone's: set on failure only
}

ExplainDone lands an explain.

type GridView

type GridView struct {
	Result   *model.Result // the result the grid holds
	Hidden   []int         // result columns hidden in the grid, ascending
	SortCol  int           // the result column sorted on; -1 for the result's own order
	SortDesc bool
	Order    []int // display row → result row; read, never kept
}

GridView is how a UI's grid shows the result it holds — presentation, so it stays with each UI, but the assistant's data rule needs it: rows go in the order the user sees them, and hidden columns stay out.

type Job

type Job func() Event

Job is the blocking half of a request: it does the work, lands the outcome in the workspace, and returns an Event describing what landed (nil when there is nothing to tell). Run it off the UI goroutine.

type Level

type Level int

Level is how a Note should be shown — the TUI's log colors, the web log's classes.

const (
	Info   Level = iota // neutral progress: "running query on pg — SELECT …"
	Ok                  // something finished well
	Warn                // worth a look, not a failure: stopped, busy, lost state
	Err                 // a failure
	Accent              // a pointer to something new on screen
	Muted               // background detail
)

type Note

type Note struct {
	Level Level
	Text  string
}

Note is one line for the user's log. The workspace writes the words, so a busy refusal or a stopped run reads the same in the terminal and the browser.

func PlanNotes

func PlanNotes(p *explain.Plan, prev *explain.Plan, elapsed time.Duration) []Note

PlanNotes is what the log says when a plan lands: how long the explain took and the plan's headline, how it compares with prev (the last plan of the same statement, or nil), and each finding worth acting on.

type Options

type Options struct {
	// Sink receives the events that happen mid-run, from the run's own
	// goroutine: a script's ScriptShow and ScriptPrint. It must not block
	// for long, and must not call back into the Workspace's start methods
	// synchronously. nil drops them.
	Sink func(Event)

	// WholeCatalog is for a UI with no schema picker (the TUI): a connect
	// that names no schema lists every schema's tables, as long as the
	// database has at most db.AllSchemasLimit of them, instead of opening
	// on the search_path's first schema. Past the limit it opens on that
	// schema all the same, with a note: listing a huge catalog is the cost
	// the per-schema load exists to avoid.
	WholeCatalog bool
}

Options configure New beyond the config.

type Reason

type Reason int

Reason is why a request was refused — what the web layer maps to a status code (Busy → 409, the rest → 400).

const (
	Busy         Reason = iota + 1 // a run is already in flight
	NoConnection                   // no active connection
	Nothing                        // nothing to run or explain
	Invalid                        // the request cannot be carried out as asked
)

type Refusal

type Refusal struct {
	Reason Reason
	Note   Note
}

Refusal is a request the workspace declined, with the reason in words for the user. It is an error, but not a failure: nothing was attempted, and nothing about the workspace changed.

func (*Refusal) Error

func (r *Refusal) Error() string

type RowCounts added in v0.3.0

type RowCounts struct {
	Conn   string
	Counts map[db.TableRef]db.RowCount // read-only; see db.Manager.RowCounts
	// Stale reports counts for a connection that is no longer the active
	// one, or a counting a later connect canceled: nothing landed, and a UI
	// draws nothing.
	Stale bool
	Notes []Note
}

RowCounts lands the row counts of the active connection's tables. The counts are also kept in the workspace (see Workspace.RowCounts), so a UI that redraws its tables list later finds them there.

type RunDone

type RunDone struct {
	Tag     string
	Conn    string
	Stmts   []string      // what ran; nil for a script
	Result  *model.Result // the last statement's result; nil on failure and for a script
	Plan    *explain.Plan // Result is itself a query plan (an EXPLAIN the user typed)
	Err     error         // the failure, or db.ErrCanceled when stopped
	Script  bool
	Elapsed time.Duration
	Stale   bool // a straggler from a run already written off: draw nothing
	Notes   []Note
	// Status is for the status bar when the run failed or stopped. On
	// success it is "": the summary depends on how many rows the UI shows
	// (see ResultStatus).
	Status string
	// Counts, when non-nil, refreshes the sidebar's row counts: the run may
	// have changed rows (db.ChangesRows), failed or not. Its event is a
	// *RowCounts, as Connected.Counts's is.
	Counts Job
}

RunDone lands a run of statements, or of a script.

type SchemaLoaded added in v0.3.0

type SchemaLoaded struct {
	Conn    string
	Schema  string        // the schema loaded; "" for every schema
	Catalog *model.Result // its tables; nil when the load failed, and the old list stays
	// Stale reports a load superseded by a later pick or connect: nothing
	// landed, and a UI draws nothing.
	Stale bool
	Notes []Note
	// Counts is the row counting of the new tables, as on Connected.
	Counts Job
}

SchemaLoaded lands a schema pick (Workspace.PickSchema): the tables of another schema of the active database, in place of the ones the sidebar listed.

type SchemaPick added in v0.3.0

type SchemaPick struct {
	// Name is the schema to list. One the database no longer has falls
	// back to the default rather than to an empty list.
	Name string
	// All lists every schema's tables, where the database has at most
	// db.AllSchemasLimit of them; past that, the default schema, with a
	// note saying why.
	All bool
}

SchemaPick says which tables a sidebar on a db.Navigable driver lists (Postgres: see db/navigate.go). The zero value is the default: the first schema on search_path, which is what a bare table name means there.

type ScriptPrint

type ScriptPrint struct{ Text string }

ScriptPrint is a script's s.Print, mid-run: a line for the log.

type ScriptShow

type ScriptShow struct{ Result *model.Result }

ScriptShow is a script's s.Show, mid-run: a result to put in the grid.

type SessionReleased

type SessionReleased struct {
	Conn     string
	Stateful bool   // it held state, now gone
	Notes    []Note // a warning when it did
}

SessionReleased reports that switching connections closed the session pinned to the previous one.

type Start

type Start struct {
	Tag   string // names the run: "query", "statement 2/4", "explain", …
	Gen   int    // the run generation, for a ticker (see Ticking)
	Notes []Note // what to log now — present even when the request was refused
	Job   Job    // the blocking half; nil when refused or when there is nothing to do
}

Start is what a start method returns.

type Workspace

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

Workspace is one person's working state. Its methods are safe for concurrent use.

func New

func New(cfg *config.Config, mgr *db.Manager, hist *userdata.History, opt Options) *Workspace

New builds a workspace over cfg's connections. It does no IO: the first connect is the caller's Connect(w.Active()).

The active connection starts as the config's default_connection, or the first connection when that names none — so a UI can draw it before the connect completes.

func (*Workspace) Active

func (w *Workspace) Active() string

Active is the active connection's name.

func (*Workspace) Busy

func (w *Workspace) Busy() bool

Busy reports whether a run (statements, an explain or a script) is in flight.

func (*Workspace) Cancel

func (w *Workspace) Cancel() (n Note, status string)

Cancel is Ctrl+K and the Stop button: it stops the run in flight or, with none, the connect in flight. The Note says what it did; status is for a status bar ("" leaves it). The stopped work still lands, as stopped.

func (*Workspace) CancelConnect

func (w *Workspace) CancelConnect() (Note, bool)

CancelConnect abandons the connect in flight. It reports false when there is none. The connect still lands, as canceled.

func (*Workspace) Catalog

func (w *Workspace) Catalog() *model.Result

Catalog is the active connection's tables, as its catalog query returned them; nil before the first connect completes, or when the query failed.

func (*Workspace) CatalogSchema added in v0.3.0

func (w *Workspace) CatalogSchema() string

CatalogSchema is the schema whose tables Catalog holds, or "" when it holds every schema's (always, on a driver that is not db.Navigable).

func (*Workspace) ChatContext

func (w *Workspace) ChatContext(question string, ed Editor, view GridView) (ctx ai.Context, refs []db.TableRef)

ChatContext gathers what the next question may carry — see package ai for what actually goes, which is decided by the connection's ai_rows.

Which statement is "this query": the one under the editor's caret, since that is what the user is looking at. When it is the statement that last ran, its result (or error) comes along; when the editor is empty, the last run's statement stands in.

question is the question being (or about to be) asked; its words, like the statement's, pick which tables' schema goes along. The Tables come back named but without columns — the caller looks those up (they cost a catalog query) — and refs is the same tables as the catalog knows them, for that lookup.

It is cheap enough to call every frame, as the TUI's context chip does.

func (*Workspace) Close

func (w *Workspace) Close()

Close stops what is in flight and releases the pinned session, rolling back whatever it left open. It waits for a statement still running to return from its cancel.

func (*Workspace) Complete added in v0.4.0

func (w *Workspace) Complete(buffer string, caret int) (res sqlcomplete.Result, ready bool)

Complete suggests what to type at caret (a byte offset into buffer), from the active connection's cached schema. It never blocks on the database: ready is false when the schema is not cached yet, and the caller then runs LoadCompletions off its event loop and asks again. With no connection, or after a failed load, it is ready with the dialect's vocabulary alone.

func (*Workspace) Config

func (w *Workspace) Config() *config.Config

Config is the configuration the workspace was built with.

func (*Workspace) Connect

func (w *Workspace) Connect(name string) Start

Connect opens the connection and loads its sidebar, opening on the default schema (see SchemaPick, Options.WholeCatalog).

func (*Workspace) ConnectPick added in v0.3.0

func (w *Workspace) ConnectPick(name string, pick SchemaPick) Start

ConnectPick opens the connection and fetches its catalog for a sidebar. The catalog goes through the pool, not the pinned session: it is the app's query, and must not land inside a transaction the user has open.

On a db.Navigable driver the catalog is three short reads rather than one long one — the server's databases, this database's schemas, and the tables of the one schema pick names — so a connect to a big server costs what its sidebar shows, not what the server holds. Elsewhere it is the whole table list, as it always was — after, on MySQL, the server's databases (db.HasDatabases), for the database picker.

The connect runs under its own context, so Cancel can abandon it — without that, only connect_timeout bounds it, and with connect_timeout = "0" an unreachable host would hold "connecting…" until the OS gave up on the TCP connect, over a minute later. Starting a connect cancels any still in flight: the user has changed their mind, and the older one must not land after the newer one and switch the connection back.

pick A ──► connGen=1, dial A ───────────── (canceled) ──► lands Stale
pick B ──► connGen=2, cancel A, dial B ─► lands, active = B

The Job's event is a *Connected.

func (*Workspace) Connecting

func (w *Workspace) Connecting() (name string, ok bool)

Connecting reports whether a connect is in flight, and to what.

func (*Workspace) Databases added in v0.3.0

func (w *Workspace) Databases() []db.DatabaseInfo

Databases is the active connection's server's databases, for a database picker; nil on a driver without databases (db.HasDatabases), or when the listing failed. The slice is shared: read it, never write to it.

func (*Workspace) Derived added in v0.3.0

func (w *Workspace) Derived(name string) bool

Derived reports whether name is a connection derived onto another of its base's databases ("<conn>/<database>", config.DatabaseSep) rather than a configured one.

It is what a UI asks of Connected.Left before closing the pool a switch left. A derived pool is the one worth closing: browsing a server's databases opens one per database picked, and without a close each stays open (its *sql.DB until dbc exits, its idle connections until conn_idle_timeout), so the count grows with the browsing rather than with the configured connections. A configured connection's pool is kept, as it always was: there is one per row of the Connections list, and switching back to it should not dial again.

func (*Workspace) Diagram added in v0.3.0

func (w *Workspace) Diagram(ctx context.Context, sel erd.Selection) (*erd.Schema, error)

Diagram reads the active connection's schema and narrows it to sel — the entity-relationship diagram both UIs draw (the TUI saves and opens it, dbc web shows it and serves its files). It blocks for the catalog round trips, so a UI calls it off its event loop.

Like LookupColumns it runs on the POOL, not the pinned session: a diagram must not wait for a long query the user is running, nor read the schema from inside their open transaction.

It does not take the run slot either — reading the catalog is not a run, and a diagram while a query runs is a reasonable thing to want.

A table in sel that is not in the schema is refused (Invalid) rather than dropped quietly: a diagram "around orders" that silently shows the whole database would be a confusing answer.

A diagram of everything (sel naming no tables) is of the schema the sidebar lists, when it lists one (CatalogSchema): on a big database "all" means what the user is looking at, not every table the server holds.

func (*Workspace) Disconnect added in v0.3.0

func (w *Workspace) Disconnect() (left string, st Start, err error)

Disconnect leaves the active connection without picking another: no active connection, no catalog, and — through the Job — the pinned session closed, rolling back whatever it left open, as a switch does. A connect in flight is abandoned with it (connGen moves on, so it lands Stale), as is the sidebar's counting and schema loading. It returns the connection left: on a connect still dialing, the one being dialed when there was no connection before it.

A run in flight is refused rather than canceled: its session is the one the disconnect would close, and a Stop first leaves the user to decide what becomes of the statement. Nothing to disconnect from is refused too.

The pool is not this workspace's to close — other workspaces may be on the same connection (db.Manager is shared); a UI that knows they are not closes it with db.Manager.Disconnect once the Job has run.

The Job's event is a *SessionReleased, or nil when no session was open.

func (*Workspace) Explain

func (w *Workspace) Explain(stmt, where string, analyze bool) (Start, error)

Explain explains one statement on the active connection. where is how the statement was picked ("statement 2/4", "selection"), folded into the tag; "" or "query" adds nothing. The Job's event is an *ExplainDone.

func (*Workspace) ExplainEditor

func (w *Workspace) ExplainEditor(ed Editor, analyze bool) (Start, error)

ExplainEditor is Ctrl+X (estimate) and Alt+X (analyze): explain the statement under the caret, or the selected one.

func (*Workspace) History

func (w *Workspace) History() *userdata.History

History is the query history runs are recorded in.

func (*Workspace) LastErr

func (w *Workspace) LastErr() string

LastErr is what the last run failed with, "" if it worked. A stopped run leaves it alone: stopping is the user's choice, not a failure to explain.

func (*Workspace) LastResult

func (w *Workspace) LastResult() *model.Result

LastResult is the last result published — by a run, or by a script's s.Show. nil before any.

func (*Workspace) LastStmt

func (w *Workspace) LastStmt() string

LastStmt is the statement the last run executed ("" before any).

func (*Workspace) ListTables

func (w *Workspace) ListTables() (Start, error)

ListTables is Ctrl+T: the active driver's catalog query, through the same path a run takes, so it lands in the grid and exports like any result. It is the app's query, not the user's, so it is not recorded.

func (*Workspace) LoadCompletions added in v0.4.0

func (w *Workspace) LoadCompletions(ctx context.Context) error

LoadCompletions reads the active connection's schema into the completion cache, unless it is cached already. It blocks for the catalog round trips (bounded by CompletionTimeout and ctx), so a UI calls it off its event loop. Callers that arrive while a load is in flight wait for that one rather than starting their own.

The error is the load's, for the UI to report once; completion goes on with the vocabulary alone either way.

func (*Workspace) LookupColumns

func (w *Workspace) LookupColumns(refs []db.TableRef) ([][]db.Column, error)

LookupColumns fetches the columns of the tables a question involves (the refs ChatContext returned) on the active connection, under SchemaLookupTimeout. It blocks for a catalog round trip, so a UI calls it off its event loop; the result goes to AttachColumns.

It runs on the POOL, not the pinned session: the session may be mid-run, and a question about a slow query must not wait for that query to end.

func (*Workspace) Manager

func (w *Workspace) Manager() *db.Manager

Manager is the connection manager the workspace runs on.

func (*Workspace) PickSchema added in v0.3.0

func (w *Workspace) PickSchema(pick SchemaPick) (Start, error)

PickSchema lists another schema's tables in the sidebar, on the active connection, in place of the ones listed now. The tables load off the UI's event loop (the Job), like a connect's; the event is a *SchemaLoaded, with a Counts job for the new tables' row counts.

It shares connGen with Connect, so whichever of a pick and a connect starts later wins: a connect supersedes a pick in flight (another database's schemas are not this one's), and a pick supersedes the old list's counting and any earlier pick. A pick while a connect is still in flight is refused — the schema list it would pick from is being replaced.

pick sales ──► gen=5, load sales ───────────── (canceled) ─► Stale
pick hr    ──► gen=6, cancel sales, load hr ─► lands, schema = hr

func (*Workspace) Plan

func (w *Workspace) Plan() *explain.Plan

Plan is the last plan: an explain's, or one recognized in a result.

func (*Workspace) Recount added in v0.4.0

func (w *Workspace) Recount(conn string) Job

Recount refreshes the sidebar's row counts after another workspace's run on conn may have changed rows, or returns nil when this sidebar is not showing conn's tables. A UI that hosts several workspaces on one Manager (dbc web's query tabs and windows) calls it on the others when a RunDone carries Counts, so every sidebar on the connection shows the write, not only the one that made it.

Unlike recountLocked it does not drop the Manager's cached counts: the writer's landRun already did, before its RunDone was delivered, so what is cached now was counted after the write. Dropping again would only make each workspace count afresh instead of sharing the writer's counting (db.Manager.RowCounts waits for one in flight).

func (*Workspace) RowCounts added in v0.3.0

func (w *Workspace) RowCounts() map[db.TableRef]db.RowCount

RowCounts is the row count of each of the catalog's tables that has one (views never do); nil until the connect's counting lands. The map is shared: read it, never write to it.

func (*Workspace) Run

func (w *Workspace) Run(stmts []string, tag string) (Start, error)

Run executes statements in order on the pinned session, stopping at the first failure, and publishes the last result. The Job's event is a *RunDone.

func (*Workspace) RunEditor

func (w *Workspace) RunEditor(ed Editor, all bool) (Start, error)

RunEditor is Ctrl+R (all false) or Ctrl+Shift+R (all true) on what the editor holds.

func (*Workspace) RunProgress

func (w *Workspace) RunProgress() (step, steps int)

RunProgress is how far a multi-statement run has got: the statement executing now (1-based) and how many there are. steps is 0 when idle, and for work that is not a list of statements.

func (*Workspace) RunScript

func (w *Workspace) RunScript(path string) (Start, error)

RunScript runs a Go script. Its s.Show and s.Print fire from the script's goroutine mid-run and go to the sink as ScriptShow and ScriptPrint; the Job's event is a *RunDone with Script set.

func (*Workspace) RunStmts

func (w *Workspace) RunStmts(stmts []string, tag string) (Start, error)

RunStmts records statements in the history and runs them. It is how anything the user asked to run by hand gets run: Ctrl+R, run all, a table preview.

func (*Workspace) RunTag

func (w *Workspace) RunTag() string

RunTag names the run in flight ("statement 2/4", "explain", "script x.go"); "" when idle.

func (*Workspace) RunningStatus

func (w *Workspace) RunningStatus() string

RunningStatus is the status line for the run in flight, "" when idle.

func (*Workspace) Schemas added in v0.3.0

func (w *Workspace) Schemas() []db.SchemaInfo

Schemas is every schema of the active database, empty ones included, with its table count; nil on a driver that is not db.Navigable. The slice is shared: read it, never write to it.

func (*Workspace) Session

func (w *Workspace) Session() (conn string, stateful bool)

Session describes the pinned session: the connection it is pinned to ("" when none is) and whether it holds state a close would lose — an open transaction, a SET, a temp table. A UI shows the latter as a "transaction open" badge.

It takes sessMu, so it waits for a statement in flight to finish; call it from a UI only where that is acceptable (not every frame).

func (*Workspace) ShowColumns added in v0.3.0

func (w *Workspace) ShowColumns(qname string) (Start, error)

ShowColumns lists a table's information_schema.columns rows in the grid, where they can be read, sorted and copied like any result. qname is the name the sidebar shows for the table (see db.TableIndex.Lookup).

It goes through RunStmts, like a table preview: the statement is recorded in the history, because it is what the user would have typed and is the easiest way back to it (or to an edited SELECT * of it). It is not put in the editor, whose text is the user's.

Only a table in the active connection's catalog is accepted. The name is inlined into SQL, so a name from anywhere else is refused, not escaped and tried.

func (*Workspace) Stop

func (w *Workspace) Stop()

Stop cancels the run, the connect and the row counting in flight, if any, without a word — the first step of quitting. The canceled work still lands its outcome.

func (*Workspace) Switch

func (w *Workspace) Switch(name string) Start

Switch is picking a connection: a connect with a line for the log, and nothing at all when name is already active and its catalog loaded.

func (*Workspace) SwitchPick added in v0.3.0

func (w *Workspace) SwitchPick(name string, pick SchemaPick) Start

SwitchPick is Switch, opening the sidebar on pick: a UI that remembers a schema per connection asks for it here, rather than loading the default schema's tables only to replace them with a PickSchema at once.

func (*Workspace) TableIndex

func (w *Workspace) TableIndex() *db.TableIndex

TableIndex is the catalog indexed for name lookups; nil without one.

func (*Workspace) Ticking

func (w *Workspace) Ticking(gen int) (status string, ok bool)

Ticking is the status line for run gen while it is still in flight — its tag and elapsed time — and false once it has ended, which is a ticker's cue to stop re-arming.

Jump to

Keyboard shortcuts

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