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

database

package
v0.2.3 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package database owns connections, statement classification, execution and schema introspection.

Index

Constants

View Source
const (
	// DefaultExportBatchSize is the number of rows emitted per INSERT statement.
	DefaultExportBatchSize = 100
	// MaxExportBatchSize bounds memory use and SQL statement size.
	MaxExportBatchSize = 1000
	// DefaultMaxExportRows bounds MCP exports when no explicit operator limit is set.
	DefaultMaxExportRows = 100000
)
View Source
const (
	// CodePermissionDenied means the statement needs a higher permission level.
	CodePermissionDenied = "PERMISSION_DENIED"
	// CodeStatementRefused means the statement is refused at every level.
	CodeStatementRefused = "STATEMENT_REFUSED"
)

Error codes are stable identifiers a client or model can branch on without depending on the exact wording of the message.

View Source
const DefaultMaxCellChars = 500

DefaultMaxCellChars caps the characters returned for a single text value.

A row cap alone does not bound the response: one TEXT or JSON column can hold megabytes, so `SELECT * FROM documents` can blow a model's context window in a single call even at a small row limit.

View Source
const DefaultMaxRows = 500

DefaultMaxRows caps the rows materialized for a single query when the caller does not ask for a specific limit. Results are buffered in memory, so an unbounded SELECT on a large table would otherwise blow up the process.

View Source
const DefaultPermission = PermissionReadOnly

DefaultPermission is used when nothing else is configured.

Variables

View Source
var DefaultPool = PoolConfig{
	MaxOpen:     3,
	MaxIdle:     3,
	MaxLifetime: 15 * time.Minute,
}

DefaultPool is used for every field left at zero.

View Source
var Drivers = []string{"pgx", "odbc", "godror", "sqlite3", "sqlserver"}

Drivers are the database/sql driver names registered by the blank imports above.

View Source
var ErrUnknownConnection = errors.New("unknown connection")

ErrUnknownConnection is returned when a named connection is not configured.

Formats lists the accepted output formats.

Functions

func ConnectDB

func ConnectDB(ctx context.Context, dbType, dbSource string, pool PoolConfig) (*sqlx.DB, error)

ConnectDB opens a pool and verifies it with a ping.

func NormalizeExportDialect added in v0.2.3

func NormalizeExportDialect(dialect string) (string, error)

NormalizeExportDialect validates a target dialect and returns its canonical name.

func Print

func Print(w io.Writer, res *Result) error

Print renders a Result as an ASCII table, the REPL output format.

func SplitComplete added in v0.2.2

func SplitComplete(sql string) ([]string, string)

SplitComplete returns the statements terminated by a top-level semicolon and the unterminated text after the last one, verbatim so a caller can keep buffering it. It is what an interactive reader needs: run what is complete, keep the rest.

func SplitStatements added in v0.2.2

func SplitStatements(sql string) []string

SplitStatements cuts a script into statements at top-level semicolons.

It runs the same lexer the classifier uses, so a semicolon inside a string, a comment or a dollar-quoted block does not end a statement. Each returned statement keeps its terminating semicolon when it had one; blank pieces are dropped.

A semicolon counts when it is top-level under either lexical dialect. The splitter does not know which engine the text is bound for, and requiring agreement would leave a MySQL user's `SELECT 'it\'s';` waiting forever for a terminator. The classifier still refuses anything that reads as a batch under either dialect, so the choice affects only where input is cut.

func ValidateDriver

func ValidateDriver(dbType string) error

ValidateDriver reports whether dbType is a driver dbq was built with.

database/sql would also reject an unknown driver, but only after the caller has handed over a connection string; checking up front keeps credentials out of driver-level error paths and gives a usable message.

func Write added in v0.2.2

func Write(w io.Writer, res *Result, format Format) error

Write renders a Result in the given format.

Table output includes the truncation notices; JSON and CSV are kept clean so they can be consumed by another program, and callers report truncation on stderr instead.

Types

type Analysis

type Analysis struct {
	// Kind is the statement category.
	Kind Kind
	// Unbounded marks a data-modifying statement whose blast radius cannot be
	// shown to be limited: no WHERE clause, a predicate that is really a
	// tautology, a multi-table target, or an INSERT that is not plain VALUES.
	Unbounded bool
	// Refused marks a statement that is rejected at every permission level,
	// because no permission level can make it safe to run here.
	Refused bool
	// Reason explains Unbounded or Refused, in terms an operator and a model
	// can both act on.
	Reason string
}

Analysis is the result of inspecting one SQL statement.

func Analyze

func Analyze(sql string) Analysis

Analyze classifies a SQL statement and measures its blast radius.

The statement is read under every lexical dialect dbq supports and the most dangerous reading wins. That matters because the same text can be a single harmless read on one engine and a two-statement batch ending in DROP TABLE on another — `SELECT 'a\'; DROP TABLE users; --'` is one string literal to MySQL and two statements to PostgreSQL. Choosing a dialect from the connection type would make a misconfigured driver name into a bypass, so both are checked.

func Authorize

func Authorize(connection, sql string, granted Permission) (Analysis, error)

Authorize checks a statement against a granted permission level.

It returns the analysis so the caller can decide between Query and Exec without re-parsing.

func (Analysis) RequiredPermission

func (a Analysis) RequiredPermission() Permission

RequiredPermission is the minimum level needed to run the statement.

The level depends on blast radius, not only on category: a bounded `DELETE ... WHERE id = 1` needs safe-write, but `DELETE FROM users` with no effective filter needs full. Without that distinction "safe-write" would permit emptying every table, which is not what the name promises.

type Column

type Column struct {
	Name       string `json:"name"`
	Type       string `json:"type"`
	Nullable   bool   `json:"nullable"`
	Default    string `json:"default,omitempty"`
	Position   int    `json:"position"`
	PrimaryKey bool   `json:"primary_key,omitempty"`
}

Column describes one column of a table.

type ConnectionDef

type ConnectionDef struct {
	Name        string
	Type        string
	Dialect     string
	Source      string
	Description string
	Permission  Permission
	// Pool tunes the connection pool. Zero fields use DefaultPool.
	Pool PoolConfig
}

ConnectionDef is a named connection profile.

func (ConnectionDef) CatalogType added in v0.2.2

func (d ConnectionDef) CatalogType() string

CatalogType returns the database dialect used for schema introspection. Most drivers identify their dialect directly; ODBC connections may override it.

type ConnectionInfo

type ConnectionInfo struct {
	Name        string     `json:"name"`
	Type        string     `json:"type"`
	Dialect     string     `json:"dialect,omitempty"`
	Description string     `json:"description,omitempty"`
	Permission  Permission `json:"permission"`
}

ConnectionInfo is the redacted view of a connection, safe to return over the API or to an MCP client. It never carries the DSN.

type ExportOptions added in v0.2.3

type ExportOptions struct {
	Dialect   string
	Table     string
	BatchSize int
	MaxRows   int
	Timeout   time.Duration
}

ExportOptions controls a streaming INSERT statement export.

type ExportResult added in v0.2.3

type ExportResult struct {
	Rows       int
	Statements int
	Columns    int
}

ExportResult describes an INSERT export without containing its data.

func ExportInserts added in v0.2.3

func ExportInserts(ctx context.Context, db *sqlx.DB, query string, w io.Writer, opts ExportOptions) (*ExportResult, error)

ExportInserts runs a read query and writes its rows as batched INSERT statements.

type ForeignKey

type ForeignKey struct {
	Column          string `json:"column"`
	ReferencedTable string `json:"referenced_table"`
	// ReferencedColumn may be empty when the catalog does not expose it.
	ReferencedColumn string `json:"referenced_column,omitempty"`
}

ForeignKey is a reference from one column to another table's column.

func ListForeignKeys

func ListForeignKeys(ctx context.Context, db *sqlx.DB, driver, schema, table string) ([]ForeignKey, error)

ListForeignKeys returns the outgoing foreign keys of a table.

Without these a model writing a multi-table JOIN is guessing at join keys, which is the single most common way generated SQL goes wrong. Failure is not fatal to callers: a restricted account may not see the constraint catalog.

type Format added in v0.2.2

type Format string

Format is a CLI output rendering of a Result.

const (
	// FormatTable is the human-readable ASCII table used by the REPL.
	FormatTable Format = "table"
	// FormatJSON renders a query result as an array of row objects, and an
	// exec result as a single object, for piping into jq.
	FormatJSON Format = "json"
	// FormatCSV renders a header line and one line per row.
	FormatCSV Format = "csv"
)

func ParseFormat added in v0.2.2

func ParseFormat(s string) (Format, error)

ParseFormat normalizes a user-supplied format name.

type Kind

type Kind string

Kind is the coarse classification of a SQL statement.

const (
	// KindRead only reads: SELECT, SHOW, EXPLAIN, WITH ... SELECT.
	KindRead Kind = "read"
	// KindWrite modifies data: INSERT, UPDATE, DELETE, MERGE.
	KindWrite Kind = "write"
	// KindSchema modifies schema or privileges, or can touch the filesystem:
	// CREATE, DROP, ALTER, TRUNCATE, GRANT, COPY, SELECT ... INTO OUTFILE.
	KindSchema Kind = "schema"
	// KindSession mutates connection-local state: BEGIN, COMMIT, USE, SET.
	// Always refused, at every permission level — see Analysis.
	KindSession Kind = "session"
	// KindUnknown is a statement dbq could not classify. Requires full access.
	KindUnknown Kind = "unknown"
)

func Classify

func Classify(sql string) Kind

Classify reports only the category of a statement.

func (Kind) ReturnsRows

func (k Kind) ReturnsRows() bool

ReturnsRows reports whether a statement of this kind produces a result set, which decides between Query and Exec.

type Manager

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

Manager owns the connection pools for every configured profile.

Pools are opened lazily on first use. The HTTP server explicitly pings every enabled connection at startup; other commands only open the one they use.

func NewManager

func NewManager(defs []ConnectionDef) (*Manager, error)

NewManager builds a Manager from connection definitions. No network I/O happens here.

func (*Manager) Close

func (m *Manager) Close() error

Close shuts every open pool down. Errors are joined so one bad pool does not hide the others.

func (*Manager) DB

func (m *Manager) DB(ctx context.Context, name string) (*sqlx.DB, error)

DB returns the pool for name, opening it on first use.

func (*Manager) Def

func (m *Manager) Def(name string) (ConnectionDef, error)

Def returns the definition for name.

func (*Manager) List

func (m *Manager) List() []ConnectionInfo

List returns the redacted view of every connection.

func (*Manager) Names

func (m *Manager) Names() []string

Names returns the configured connection names, sorted.

func (*Manager) Ping

func (m *Manager) Ping(ctx context.Context, name string) error

Ping opens (if needed) and verifies the connection.

type Permission

type Permission string

Permission is the access level granted to a caller for a connection.

The levels are ordered: PermissionReadOnly < PermissionSafeWrite < PermissionFull.

const (
	// PermissionReadOnly allows only statements that cannot change data or schema.
	PermissionReadOnly Permission = "read-only"
	// PermissionSafeWrite additionally allows DML whose reach can be shown to be
	// bounded: an INSERT ... VALUES, or an UPDATE/DELETE with an effective
	// WHERE clause on a single table.
	PermissionSafeWrite Permission = "safe-write"
	// PermissionFull allows everything else: DDL, privilege changes, and writes
	// that could affect an unbounded number of rows.
	PermissionFull Permission = "full"
)

func ParsePermission

func ParsePermission(s string) (Permission, error)

ParsePermission normalizes a configured permission string.

Empty input yields DefaultPermission. The machine-readable aliases used by other MCP database servers (read_only, safe_write, high_risk_write) are accepted so client configurations can be carried over.

func (Permission) CanWrite

func (p Permission) CanWrite() bool

CanWrite reports whether the permission allows any modifying statement.

func (Permission) Min

func (p Permission) Min(other Permission) Permission

Min returns the least permissive of p and other, which is how a global ceiling is combined with a per-connection grant. Composition can only ever restrict, never widen.

func (Permission) Rank

func (p Permission) Rank() int

Rank returns the ordering value of the permission, higher is more permissive.

func (Permission) Valid

func (p Permission) Valid() bool

Valid reports whether p is a known permission level.

type PoolConfig added in v0.2.2

type PoolConfig struct {
	// MaxOpen caps open connections, idle or in use.
	MaxOpen int
	// MaxIdle caps idle connections kept for reuse.
	MaxIdle int
	// MaxLifetime retires a connection after this long, so the pool follows
	// DNS changes and failovers. Negative disables the limit.
	MaxLifetime time.Duration
	// MaxIdleTime closes a connection idle for this long. Negative disables it.
	MaxIdleTime time.Duration
}

PoolConfig tunes the database/sql pool behind one connection.

Zero means "use the default"; a negative value means unlimited (for MaxIdle, which cannot be unlimited, it means "as many as MaxOpen"). Keeping the pool small by default is deliberate: dbq is usually one of many clients of a database, and an agent can issue queries far faster than a person.

func (PoolConfig) Merged added in v0.2.2

func (p PoolConfig) Merged(fallback PoolConfig) PoolConfig

Merged returns p with every zero field taken from fallback, which is how a connection's own settings sit on top of the global ones.

type QueryOptions

type QueryOptions struct {
	// MaxRows limits the rows read from the result set. Zero means DefaultMaxRows.
	// A negative value disables the limit.
	MaxRows int
	// MaxCellChars limits the characters kept per text value. Zero means
	// DefaultMaxCellChars. A negative value disables the limit.
	MaxCellChars int
	// Timeout bounds the statement. Zero means no dbq-imposed deadline.
	Timeout time.Duration
}

QueryOptions tunes a single statement execution.

type Result

type Result struct {
	// Columns are the result set column names, in order. Nil for Exec results.
	Columns []string `json:"columns,omitempty"`
	// Rows holds the result set. Values are normalized to JSON-friendly types.
	Rows [][]any `json:"rows,omitempty"`
	// RowCount is len(Rows).
	RowCount int `json:"row_count"`
	// Truncated is true when the result set hit the row limit and more rows exist.
	Truncated bool `json:"truncated,omitempty"`
	// CellsTruncated counts values that were shortened to the cell limit.
	// Non-zero means the displayed values are prefixes, not the stored data.
	CellsTruncated int `json:"cells_truncated,omitempty"`
	// RowsAffected is set for statements executed via Exec. Nil for queries.
	RowsAffected *int64 `json:"rows_affected,omitempty"`
	// Kind is how dbq classified the statement.
	Kind Kind `json:"kind"`
	// Duration is the wall-clock execution time.
	Duration time.Duration `json:"-"`
	// DurationMS is Duration in milliseconds, for JSON consumers.
	DurationMS int64 `json:"duration_ms"`
}

Result is a driver-independent, JSON-serializable query result.

func Execute

func Execute(ctx context.Context, db *sqlx.DB, sql string, opts QueryOptions) (*Result, error)

Execute runs a single statement and returns a Result.

Statements classified as read return a row set; everything else goes through Exec so that rows-affected is reported and no result set is expected. The caller is responsible for authorizing the statement first (see Authorize).

type StatementError

type StatementError struct {
	// Code is a stable machine-readable identifier.
	Code string
	// Connection is the profile the statement was aimed at.
	Connection string
	// Kind is how dbq classified the statement.
	Kind Kind
	// Granted and Required are the effective and needed permission levels.
	// Required is empty when the statement is refused unconditionally.
	Granted  Permission
	Required Permission
	// Detail explains the specific problem, e.g. why a DELETE is unbounded.
	Detail string
	// Advice tells the caller what to do about it.
	Advice string
}

StatementError is returned when a statement is rejected by the access policy.

func (*StatementError) Error

func (e *StatementError) Error() string

Error deliberately never includes the SQL text. Errors flow to logs and to model context, and statements routinely carry data the caller should not have to think twice about echoing.

type Table

type Table struct {
	Schema string `json:"schema,omitempty"`
	Name   string `json:"name"`
	Type   string `json:"type,omitempty"`
}

Table is a table or view discovered by introspection.

func ListTables

func ListTables(ctx context.Context, db *sqlx.DB, driver, schema string) ([]Table, error)

ListTables returns the tables and views visible to the connection.

schema is optional; when empty every non-system schema is returned. The queries are driver-specific because catalog layout is not portable: information_schema exists on PostgreSQL and SQL Server, SQLite has sqlite_master, and Oracle has the ALL_* views.

type TableDetail

type TableDetail struct {
	Schema string `json:"schema,omitempty"`
	Name   string `json:"name"`
	// Type is BASE TABLE or VIEW. It matters: a model will otherwise happily
	// generate an INSERT against a view.
	Type        string       `json:"type,omitempty"`
	Columns     []Column     `json:"columns"`
	ForeignKeys []ForeignKey `json:"foreign_keys,omitempty"`
}

TableDetail is the full description of a single table.

func DescribeTable

func DescribeTable(ctx context.Context, db *sqlx.DB, driver, schema, table string) (*TableDetail, error)

DescribeTable returns the column layout of a single table.

Jump to

Keyboard shortcuts

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