Documentation
¶
Overview ¶
Package userdata is the state dbc keeps for the user between sessions: the query history, the editor's per-database SQL consoles (and the single editor buffer they replaced), the assistant's conversations and the terminal UI's schema picks, all under ~/.config/dbc.
It started life inside the former tview UI (package ui, since removed), and kept that UI's on-disk format — same paths, same JSON-lines shape — so history and the scratchpad written by older dbc builds still load.
Index ¶
- Constants
- Variables
- func BufferFile() string
- func ChatTitle(msgs []ChatMsg) string
- func ChatsDir() string
- func ConsolePath(root string, d ConsoleDB, name string) string
- func ConsoleRev(text string, exists bool) string
- func ConsolesDir() string
- func DeleteConsole(root string, d ConsoleDB, name string) error
- func FlatConsoleFile(root, host, database string) string
- func HistoryFile() string
- func LayoutFile() string
- func ListConsoles(root string, d ConsoleDB, flat string) []string
- func LoadBuffer(path string) string
- func LoadConsole(path string) (text string, exists bool)
- func LoadPicks(path string) map[string]SchemaPick
- func MovePicks(path string, rename func(conn string) (to string, ok bool)) error
- func NewChatID(now time.Time) string
- func NextConsoleName(taken []string) string
- func PicksFile() string
- func RemoveChat(dir, id string) error
- func RenameConsole(root string, d ConsoleDB, from, to string) error
- func SaveBuffer(path, text string) error
- func SaveChat(dir string, c Chat) error
- func SaveConsole(path, text string) error
- func SaveLayout(path string, l Layout) error
- func SavePick(path, conn string, pick SchemaPick) error
- func ValidConsoleName(name string) bool
- type Chat
- type ChatMeta
- type ChatMsg
- type ConsoleDB
- type Entry
- type History
- type Layout
- type LayoutTab
- type SchemaPick
Constants ¶
const ( // MaxChats caps how many conversations the directory keeps, trimmed // from the oldest. The feature is "the last few chats": an unbounded // archive would be a directory growing forever and a picker nobody can // scan. Thirty is a working week or two of questions. MaxChats = 30 // MaxChatTitle bounds a derived title, in runes. The picker row also // carries a date, a message count and sometimes a connection name, and // a title long enough to push those off the row costs more than it says. MaxChatTitle = 72 )
const DefaultConsole = "console"
DefaultConsole is the name of a database's first console.
Variables ¶
var ErrConsoleExists = errors.New("a console by that name already exists")
ErrConsoleExists is a rename onto a name the database already has.
Functions ¶
func BufferFile ¶
func BufferFile() string
BufferFile is where the editor buffer persists between sessions, under the user's config directory. It returns "" when no home directory is known, and the buffer simply does not persist.
func ChatTitle ¶
ChatTitle picks what a conversation is remembered by: the first line of the first thing the USER asked. Not the answer — the question is what the user scans a list for, and an answer's first line is usually a restatement or a preamble. Returns "" when there is no question; the caller decides how an untitled row reads.
func ChatsDir ¶
func ChatsDir() string
ChatsDir is where conversations persist, beside the history and the editor buffer. It returns "" when no home directory is known, and conversations then live only for the session.
func ConsolePath ¶ added in v0.6.0
ConsolePath is the file of console name of database d under root: "" when root is "" (persistence off) or any level is not a valid name.
func ConsoleRev ¶ added in v0.6.0
ConsoleRev is the revision of a console's text: a hash, so any writer — the TUI, another dbc web window, an editor outside dbc — changing the file changes it, with no counter to keep in step. A console with no file is revision "".
func ConsolesDir ¶ added in v0.6.0
func ConsolesDir() string
ConsolesDir is where the consoles live, beside the history and the legacy buffer. It returns "" when no home directory is known, and consoles then do not persist.
func DeleteConsole ¶ added in v0.6.0
DeleteConsole removes a console of database d. One with no file is already gone, which is success.
func FlatConsoleFile ¶ added in v0.6.0
FlatConsoleFile is where the first consoles layout kept database on host's one console, directly under root — for ListConsoles to move in.
func HistoryFile ¶
func HistoryFile() string
HistoryFile is where the history persists, beside the editor buffer. It returns "" when no home directory is known, and history lives only for the session.
func LayoutFile ¶ added in v0.8.0
func LayoutFile() string
LayoutFile is where the TUI's layout persists, beside the schema picks. It returns "" when no home directory is known, and the layout lasts only for the run.
func ListConsoles ¶ added in v0.6.0
ListConsoles is the names of database d's consoles under root, in the order they are offered: DefaultConsole first, then "console-2", "console-3", … by number, then any other name alphabetically. A database with none lists none: a console only gets a file once it holds something (SaveConsole).
flat is the database's file in the first consoles layout (one file per database: FlatConsoleFile), "" when there is none to look for; such a file is moved in as DefaultConsole here.
func LoadBuffer ¶
LoadBuffer returns the persisted editor buffer, or "" when there is none.
func LoadConsole ¶ added in v0.6.0
LoadConsole returns a console's text and whether it has a file at all. The difference matters: a console emptied on purpose stays empty, where one never written may be seeded (from the legacy buffer.sql).
func LoadPicks ¶ added in v0.3.0
func LoadPicks(path string) map[string]SchemaPick
LoadPicks returns the saved picks, keyed by connection name ("<conn>" or a derived "<conn>/<database>"). A missing or unreadable file is no picks: losing them only means a connection opens on its default schema.
func MovePicks ¶ added in v0.8.0
MovePicks renames the picks rename maps to a new name (ok true), keeping the rest — a connection renamed in the TUI's form takes its pick, and those of its other databases ("<old>/analytics"), with it. Like SavePick it re-reads the file, so another TUI's picks since startup are kept.
func NewChatID ¶
NewChatID mints a conversation id from a wall-clock instant: sortable by construction, which lets a listing fall back on id order when a file's Updated stamp is missing, and legible to a user browsing the directory.
The tail is the full nanosecond field. ⟲ new saves the old conversation and the next save mints the new one's id moments later; a coarser tail could collide there, and a collision would overwrite the earlier file.
func NextConsoleName ¶ added in v0.6.0
NextConsoleName is the name a new console gets, given the names already taken: DefaultConsole if free, else the lowest free "console-n".
func PicksFile ¶ added in v0.3.0
func PicksFile() string
PicksFile is where the TUI's schema picks persist, beside the buffer. It returns "" when no home directory is known, and picks last only for the run.
func RemoveChat ¶
RemoveChat deletes one conversation. A missing file is success: the caller wanted it gone.
func RenameConsole ¶ added in v0.6.0
RenameConsole renames a console of database d. A console that was never written has no file, and renaming it is only a change of name: nothing to move, and success.
func SaveBuffer ¶
SaveBuffer persists the editor buffer. The file is user-only: query scratchpads have a way of accumulating real values.
func SaveChat ¶
SaveChat writes c into dir as <id>.json, then trims the directory back to MaxChats. Rewriting the same id is the normal case — the live conversation is re-saved after every answer — so the write goes to a temp file that is renamed over the old one: a crash mid-write must not leave a half-written transcript where a readable one used to be.
An empty dir, id or message list is a no-op rather than an error. The caller saves unconditionally at teardown, and "there was no conversation" is not a failure.
The files are user-only, like the history: a conversation about a query carries the query, and on connections with ai_rows its result rows.
func SaveConsole ¶ added in v0.6.0
SaveConsole writes a console, user-only like the buffer it replaces. A console with nothing in it and no file yet is not written: looking at a database, or opening a console and leaving it empty, must not leave a file behind.
func SaveLayout ¶ added in v0.8.0
SaveLayout writes the layout, through a temp file renamed over the old one so a crash mid-write leaves the previous layout rather than a truncated file. Two TUIs quitting at once: the last one's layout wins, which is the one the user saw last.
func SavePick ¶ added in v0.3.0
func SavePick(path, conn string, pick SchemaPick) error
SavePick records one connection's pick in the file, keeping every other connection's.
It re-reads the file rather than writing the caller's map: two dbc TUIs can be open at once, each picking on its own connections, and writing a whole map loaded at startup would undo the other's picks since then. The write goes to a temp file renamed over the old one, so a crash mid-write leaves the old picks rather than a truncated file. (Two saves in the same instant can still lose one of them; a pick is a click, and the cost is a connection opening on its default schema once.)
The file is never pruned: an entry is a few bytes, and a connection that is gone from one config may be in another (-config).
func ValidConsoleName ¶ added in v0.6.0
ValidConsoleName reports whether name may name a console, or be one level of a ConsoleDB: all three come from the browser as path segments and become file names.
Types ¶
type Chat ¶
type Chat struct {
ID string `json:"id"`
Title string `json:"title"`
Agent string `json:"agent,omitempty"` // the backend's display name
Model string `json:"model,omitempty"` // the model's display name
Conn string `json:"conn,omitempty"` // the active connection at the last save
Started time.Time `json:"started"`
Updated time.Time `json:"updated"`
Msgs []ChatMsg `json:"msgs"`
}
Chat is one archived conversation.
func (Chat) RestoreNote ¶
RestoreNote is the transcript line shown when a saved conversation is reopened: what the user got back, and what they did not. A reopened conversation is a transcript, not a resumed agent session, and silence about that would let a follow-up get a confidently unrelated answer from a model that never saw the conversation. Both UIs show these words.
type ChatMeta ¶
type ChatMeta struct {
ID string
Title string
Agent string
Conn string
Updated time.Time
Count int // messages, so a row can say how much is in there
}
ChatMeta is the listing view: what a picker row needs, without the transcript. ListChats still reads whole files — they are few and small, and a partial parse would need a second schema to stay honest — but returning only this keeps a caller from holding every conversation in memory to draw a list of titles.
func ListChats ¶
ListChats returns the archived conversations newest first. A file that will not parse costs itself and nothing else: one truncated or hand-edited transcript must not take the whole list down with it. A missing directory is the common case — a user who has never asked anything — and is not an error.
type ChatMsg ¶
ChatMsg is one transcript entry. Role is a string ("user", "agent", "tool", "note", "info", "error") rather than the TUI's integer: an archive outlives the enum, and a reordered constant block must not silently restyle every stored conversation.
type ConsoleDB ¶ added in v0.6.0
ConsoleDB names one database's directory of consoles: Host, then Database, each one directory level (see the layout above). Both are directory names, not the host and database as the driver knows them — ConsoleDBOf makes one from those.
func ConsoleDBOf ¶ added in v0.6.0
ConsoleDBOf is the directory of database on host. host may be "" (a connection whose DSN could not be read — see db.ConsoleTarget).
func (ConsoleDB) Key ¶ added in v0.8.0
Key is d as one string, "host/database": its two directory levels, which ConsoleDBOf already made file-name safe, so neither holds a "/". It is how a history entry records its database (Entry.DB) — stable across connection renames, the same in the TUI and dbc web, and comparable as a plain string. The zero ConsoleDB's key is "".
type Entry ¶
type Entry struct {
At time.Time `json:"at"`
Conn string `json:"conn"`
SQL string `json:"sql"`
// DB is the database the statement ran on, as ConsoleDB.Key — the same
// host ─► database identity the consoles are kept by, so "this
// database's history" and "this database's consoles" agree, and two
// connection names onto one database share both. It is a key rather
// than the connection's name because a name is only a label (renaming
// "prod" must not orphan its history) and one server holds many
// databases.
//
// omitempty leaves entries written before the field existed as they
// were, and an older dbc reading a new line ignores the unknown field.
// "" means "database unknown": such an entry shows only under "all
// databases", never under a database it might not belong to.
DB string `json:"db,omitempty"`
}
Entry is one statement the user ran — one line of the history file.
func HistoryIn ¶ added in v0.8.0
HistoryIn keeps the entries recorded on database key (Entry.DB). An empty key keeps everything: with no database to scope to (no connection, consoles off), "this database" can only mean all of them. Entries with no DB — written before the field existed — are in no database's scope; they show under "all".
func MatchHistory ¶
MatchHistory keeps the entries whose SQL or connection name holds q, case-insensitively. An empty query keeps everything.
type History ¶
type History struct {
// contains filtered or unexported fields
}
History is the statements the user has run, oldest first, backed by a JSON-lines file: one object per line carries the multi-line SQL that a plain-text history would have to escape, and appending one entry costs one write rather than a rewrite of the file.
It is safe for concurrent use. The TUI touches it from one goroutine, but `dbc web` gives every browser tab its own workspace over ONE shared History (so a query run in any tab, or in the terminal's history file, is recallable from all of them), and those tabs record from their own request goroutines. mu also serializes the file appends, so two tabs recording at once cannot interleave half-lines.
func LoadHistory ¶
LoadHistory reads the history file, skipping lines it cannot parse — a half-written last line from a killed process must not cost the whole history. An over-long file is trimmed back to histMax here, which is the only point at which the file is rewritten rather than appended to.
func (*History) Add ¶
Add records a statement, unless it repeats the one just recorded — running the same query twice while watching a table change should not bury the rest of the history. The entry is kept in memory whether or not the file write works, so a read-only config directory costs persistence, not the session.
func (*History) AddEntry ¶ added in v0.8.0
AddEntry is Add with the whole entry given — the database (Entry.DB) included, which the workspace knows and Add's callers need not.
The repeat check compares the database too: the same query run on one database and then on another is two entries, or the second database's scoped history would be missing a statement that ran on it.
type Layout ¶ added in v0.8.0
type Layout struct {
SideW int `json:"side_w,omitempty"`
ChatW int `json:"chat_w,omitempty"`
LogH int `json:"log_h,omitempty"`
EdFrac float64 `json:"ed_frac,omitempty"`
SideHidden bool `json:"side_hidden,omitempty"`
// Tabs are the query tabs, in strip order, and ActiveTab the index of
// the one on screen. Empty from an older dbc: one tab, as before.
Tabs []LayoutTab `json:"tabs,omitempty"`
ActiveTab int `json:"active_tab,omitempty"`
}
Layout is the terminal UI's pane geometry as the user left it: the sizes dragged at the pane borders and whether the sidebar was folded away (^B). dbc web keeps the same things in its store's layout; the TUI has no store, so, like the schema picks, they get a small file of their own.
Zero means "never set": New keeps its default for a zero field, so a file from an older dbc (or one missing a field) costs only that field.
func LoadLayout ¶ added in v0.8.0
LoadLayout returns the saved layout. A missing or unreadable file is the zero Layout: the defaults, which is all losing it costs.
type LayoutTab ¶ added in v0.8.0
type LayoutTab struct {
Title string `json:"title"`
Conn string `json:"conn,omitempty"`
Console string `json:"console,omitempty"`
}
LayoutTab is one saved query tab: what it is called, the connection it was on, and the console it showed (a name within that connection's database's consoles). The console's text is in its file, not here.
type SchemaPick ¶ added in v0.3.0
SchemaPick is the schema the terminal UI's Tables pane was last on for one connection: Name, or every schema (All). It mirrors workspace.SchemaPick rather than importing it, so this package stays below workspace.
dbc web keeps its picks in its own store's layout ("tableSchema.<conn>"); the TUI has no store, so its picks get a file of their own here.