Documentation
¶
Overview ¶
Package dbquery runs one read-only SQL statement against a Postgres and returns typed, bounded, JSON-encodable results. It is the execution core behind `forge db query`: forge's local transport and the control plane's hosted QueryDatabase both call Run, so a query means the same thing wherever it runs.
What every query does ¶
- Check, the lexical guard: exactly one statement, and one that starts SELECT, WITH, TABLE, VALUES, SHOW or EXPLAIN. It exists for good error messages and is NOT the security boundary.
- BEGIN TRANSACTION READ ONLY ISOLATION LEVEL REPEATABLE READ, then statement_timeout, lock_timeout and idle_in_transaction_session_timeout set for that transaction only.
- The statement goes over the extended protocol, which refuses a multi-statement string on its own, and its rows are read up to the cap.
- ROLLBACK, always.
The boundary is the read-only transaction, which refuses every write, nextval() and a data-modifying CTE, plus the role's privileges, which the caller owns (a role that cannot SELECT a column cannot read it through any expression). The context deadline is a client-side backstop to the server-side timeout: a SELECT can call set_config('statement_timeout', '0') on its own transaction.
Bounds ¶
Rows are capped (default 200, at most 10,000), the encoded response is capped at 4 MiB, a single cell at 4 KiB, and a query's time at 5 s by default and 30 s at most. A result cut by a cap is not an error: it carries Truncated and says which cap.
Audit ¶
An Auditor sees every attempt that gets past argument validation, the ones the guard refuses included, so an attempted write against production is on record too. A failing Auditor withholds the rows.
This package opens no connections and owns no credentials. The caller passes anything with a pgx BeginTx (a *pgx.Conn, a *pgxpool.Pool).
Index ¶
Constants ¶
const ( DefaultMaxRows = 200 MaxRowsLimit = 10_000 DefaultTimeout = 5 * time.Second MaxTimeout = 30 * time.Second // MaxCellBytes is the longest text a single cell keeps; the rest is // replaced by a marker. MaxCellBytes = 4 << 10 // MaxResponseBytes caps the encoded size of all cells in a result. MaxResponseBytes = 4 << 20 )
Limits. The Default* values apply to a zero Options field; a value above its Max* is refused, not clamped, so a caller learns the cap it asked past.
const MaxSQLBytes = 16 << 10
MaxSQLBytes caps the statement text.
Variables ¶
This section is empty.
Functions ¶
func Check ¶
Check is the lexical guard: the first line of defence, and the source of good error messages. It accepts exactly one statement that starts with SELECT, WITH, TABLE, VALUES, SHOW or EXPLAIN, and refuses EXPLAIN ANALYZE over anything but a SELECT, because ANALYZE executes what it explains.
IT IS NOT THE SECURITY BOUNDARY. A WITH can still carry a data-modifying CTE and a SELECT can still call nextval(); the read-only transaction Run opens is what refuses them, and the role's privileges are what refuse everything else. The guard is pure Go (no cgo, no parser dependency) so the forge binary stays cross-compilable; it understands comments, quoted identifiers, string literals (including E” and dollar quoting) well enough to find the statement's end, and no more.
On success it returns the statement's leading keyword, upper-cased.
Types ¶
type Auditor ¶
Auditor records one query attempt. It runs after the attempt, once, on a context that survives the caller's cancellation. If it returns an error, Run returns KindAudit and no rows, unless the attempt itself already failed, in which case that failure is returned.
type Beginner ¶
Beginner is what Run needs from a connection. *pgx.Conn and *pgxpool.Pool both satisfy it.
type Column ¶
type Column struct {
Name string `json:"name"`
// PGType is Postgres' name for the type ("int4", "timestamptz", an enum's
// name).
PGType string `json:"pg_type"`
}
Column describes one result column.
type Error ¶
type Error struct {
Kind Kind
// Reason is a stable machine-readable subcode within Kind
// ("multiple_statements", "statement_timeout", ...), empty when Kind says
// it all.
Reason string
// Message is safe to show the caller who wrote the SQL.
Message string
// Err is the underlying cause, if any.
Err error
}
type Event ¶
type Event struct {
SQL string
SQLSHA256 string
// Leader is the statement's leading keyword, empty when the guard
// refused before finding one.
Leader string
// Outcome is "ok", or the [Kind] of the failure.
Outcome string
// Err is the failure, nil on success.
Err error
RowCount int
Bytes int
Truncated bool
StartedAt time.Time
Duration time.Duration
MaxRows int
TimeoutSec float64
}
Event is what an Auditor receives.
type Kind ¶
type Kind string
Kind classifies why a query did not return rows. A caller maps it onto its own transport (a Connect code, a CLI exit code) with one switch; the package imports neither.
const ( // KindRefused: the request was refused before it reached the database — // the lexical guard, an empty or oversized statement, or limits out of // range. The request is wrong; retrying it unchanged cannot help. KindRefused Kind = "refused" // KindReadOnly: the database refused a write inside the read-only // transaction. The guard let the statement through (or was bypassed); the // transaction is the real boundary, and this is it holding. KindReadOnly Kind = "read_only" // KindPermission: the role may not read something the statement named // (SQLSTATE 42501) — a column marked forge:secret, for example. KindPermission Kind = "permission_denied" // KindTimeout: the statement outran its timeout, or waited past the lock // timeout. KindTimeout Kind = "timeout" // KindCanceled: the caller's context was canceled. KindCanceled Kind = "canceled" // KindSQL: the database rejected the statement itself — a syntax error, an // unknown column, a multi-statement string reaching the extended protocol. KindSQL Kind = "sql_error" KindUnavailable Kind = "unavailable" // KindAudit: the [Auditor] failed. The rows are withheld. KindAudit Kind = "audit_failed" )
type Options ¶
type Options struct {
// MaxRows is the row cap. 0 means DefaultMaxRows; above MaxRowsLimit is
// refused.
MaxRows int
// Timeout is the statement timeout. 0 means DefaultTimeout; above
// MaxTimeout is refused.
Timeout time.Duration
// Auditor, when set, is told how every attempt went.
Auditor Auditor
// contains filtered or unexported fields
}
Options bound one query. The zero value is the default policy.
type Result ¶
type Result struct {
Columns []Column `json:"columns"`
Rows [][]any `json:"rows"`
// RowCount is len(Rows).
RowCount int `json:"row_count"`
// Truncated is true when a cap cut the result; TruncatedBy names it:
// "rows" or "bytes".
Truncated bool `json:"truncated"`
TruncatedBy string `json:"truncated_by,omitempty"`
// CellsTruncated counts cells cut to MaxCellBytes.
CellsTruncated int `json:"cells_truncated,omitempty"`
DurationMS int64 `json:"duration_ms"`
// Bytes is the encoded size of the cells kept, the figure the response
// cap counts.
Bytes int `json:"bytes"`
}
Result is a completed query. Each row has one cell per column, in column order. A cell is nil (NULL), a bool, a json.Number, a string, or a json.RawMessage (json and jsonb); the whole value marshals with encoding/json as is.
Numbers are exact: int2, int4 and floats are JSON numbers, and int8 and numeric are JSON numbers only up to 15 digits, a string beyond that, since a JSON reader that parses doubles would round them. Everything else, dates and times, uuid, arrays, bytea, enums, is the text Postgres prints for it.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package kubedial reaches a CloudNativePG database in a Kubernetes namespace through the kube API server, with no network path of its own: it resolves a ready pod behind the database's Service, opens a pods/portforward stream to it and hands the stream to pgx as a net.Conn.
|
Package kubedial reaches a CloudNativePG database in a Kubernetes namespace through the kube API server, with no network path of its own: it resolves a ready pod behind the database's Service, opens a pods/portforward stream to it and hands the stream to pgx as a net.Conn. |