dbviewer

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package dbviewer runs read-only (and, when explicitly allowed, write) SQL against a managed database by exec'ing the engine's own client inside its container. Credentials never leave the container.

Index

Constants

View Source
const (
	KindQuery   = "query"
	KindExplain = "explain"
	KindShow    = "show"
	KindWrite   = "write"
)

Statement kinds the guard classifies an accepted statement into.

View Source
const (
	EnvQueryTimeout  = "APP_DB_QUERY_TIMEOUT"
	EnvQueryMaxRows  = "APP_DB_QUERY_MAX_ROWS"
	EnvQueryMaxBytes = "APP_DB_QUERY_MAX_BYTES"
	EnvQueryMaxCell  = "APP_DB_QUERY_MAX_CELL_BYTES"
	EnvSchemaMaxRows = "APP_DB_SCHEMA_MAX_ROWS"
	EnvQueryMaxSQL   = "APP_DB_QUERY_MAX_SQL_BYTES"
	EnvHistoryKeep   = "APP_DB_QUERY_HISTORY_KEEP"
)

Environment variables that tune the console. Each has a default so an unset variable behaves the same on every install.

View Source
const (
	OpContains = "contains"
	OpEquals   = "equals"
	OpIsNull   = "is_null"
	OpNotNull  = "not_null"
)

Filter operators accepted by a table page.

View Source
const MaxFilters = 8

MaxFilters bounds how many column filters one page request may combine.

Variables

View Source
var (
	ErrEmpty             = errors.New("statement is empty")
	ErrMultiStatement    = errors.New("only a single statement is allowed")
	ErrBackslash         = errors.New("backslash is not allowed in console statements")
	ErrControlChar       = errors.New("statement contains a control character")
	ErrUnterminated      = errors.New("unterminated string, identifier, or comment")
	ErrExecutableComment = errors.New("executable comments are not allowed")
	ErrUnicodeEscape     = errors.New("unicode escape syntax is not allowed")
	ErrNotAllowed        = errors.New("statement type is not allowed in read-only mode")
	ErrDenied            = errors.New("statement uses a function or keyword that is not allowed")
)

Rejection reasons, exported so handlers can map them to a 400.

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound reports a table or column the database does not have.

View Source
var ErrTimeout = errors.New("query exceeded the configured timeout")

ErrTimeout reports that the statement ran past the configured timeout.

Functions

func HistoryKeepFromEnv

func HistoryKeepFromEnv(lookup func(string) (string, bool)) int

HistoryKeepFromEnv is how many history rows to keep per user and database.

Types

type Column

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

Column is one table column.

type Dialect

type Dialect string

Dialect selects the lexing and deny-list rules for one SQL family.

const (
	DialectPostgres Dialect = "postgres"
	DialectMySQL    Dialect = "mysql"
	DialectMariaDB  Dialect = "mariadb"
)

Supported dialects.

type Evidence

type Evidence struct {
	DatabaseSizeBytes int64    `json:"database_size_bytes"`
	SchemasScanned    int      `json:"schemas_scanned"`
	UserTables        int64    `json:"user_tables"`
	ExcludedSchemas   []string `json:"excluded_schemas"`
}

Evidence is what the catalog says about a database independent of the schema listing, so an empty listing can be checked against it.

type Execer

type Execer interface {
	ExecWithInput(ctx context.Context, containerID string, cmd []string, stdin io.Reader) (io.ReadCloser, error)
}

Execer is the slice of docker.Runtime the console needs.

type Filter

type Filter struct {
	Column string `json:"column"`
	Op     string `json:"op"`
	Value  string `json:"value"`
}

Filter restricts one column of a table page.

type ForeignKey

type ForeignKey struct {
	Name       string   `json:"name"`
	Schema     string   `json:"schema"`
	Table      string   `json:"table"`
	Columns    []string `json:"columns"`
	RefSchema  string   `json:"ref_schema"`
	RefTable   string   `json:"ref_table"`
	RefColumns []string `json:"ref_columns"`
}

ForeignKey is one foreign key constraint. Schema and Table name the table that owns it; RefSchema and RefTable the table it points at.

type Index

type Index struct {
	Name       string `json:"name"`
	Definition string `json:"definition"`
	Unique     bool   `json:"unique"`
	Primary    bool   `json:"primary"`
}

Index is one table index.

type Limits

type Limits struct {
	Timeout      time.Duration
	MaxRows      int
	MaxBytes     int
	MaxCellBytes int
	SchemaRows   int
	SQLBytes     int
}

Limits bounds one console execution.

func LimitsFromEnv

func LimitsFromEnv(lookup func(string) (string, bool)) Limits

LimitsFromEnv reads the limits, falling back to defaults for unset or invalid values.

type Page

type Page struct {
	Result
	HasMore bool `json:"has_more"`
}

Page is one page of rows. HasMore is true when rows exist past this page.

type PageQuery

type PageQuery struct {
	Schema, Table string
	Limit, Offset int
	SortColumn    string
	SortDesc      bool
	FilterColumn  string
	FilterOp      string
	FilterValue   string
	Filters       []Filter
}

PageQuery asks for one page of a table. FilterColumn/FilterOp/FilterValue are the first filter; Filters adds more, all combined with AND.

type QueryError

type QueryError struct{ Message string }

QueryError carries the database's own error text, which is safe to show: it describes the caller's statement and never contains credentials.

func (*QueryError) Error

func (e *QueryError) Error() string

type RedisKey

type RedisKey struct {
	Key  string `json:"key"`
	Type string `json:"type"`
	TTL  int64  `json:"ttl"`
}

RedisKey is one scanned key with its type and TTL (seconds, -1 for none).

type RedisScan

type RedisScan struct {
	Cursor string     `json:"cursor"`
	Keys   []RedisKey `json:"keys"`
}

RedisScan is one SCAN step. Cursor "0" means the scan is complete.

type RedisValue

type RedisValue struct {
	Type      string   `json:"type"`
	TTL       int64    `json:"ttl"`
	Lines     []string `json:"lines"`
	Truncated bool     `json:"truncated"`
}

RedisValue is a key's value rendered as text lines, capped by Limits.

type Result

type Result struct {
	Columns    []string    `json:"columns"`
	Rows       [][]*string `json:"rows"`
	RowCount   int         `json:"row_count"`
	Truncated  bool        `json:"truncated"`
	DurationMs int64       `json:"duration_ms"`
}

Result is one statement's output. A nil cell is SQL NULL.

type SchemaNode

type SchemaNode struct {
	Name   string  `json:"name"`
	Tables []Table `json:"tables"`
}

SchemaNode groups tables under one schema (a MySQL database).

type Statement

type Statement struct {
	// SQL is the statement with its trailing semicolon removed.
	SQL         string
	Kind        string
	Fingerprint string
	// First is the lowercased leading keyword.
	First string
}

Statement is a statement the guard accepted.

func Check

func Check(d Dialect, sql string, allowWrites bool) (Statement, error)

Check lexes sql and decides whether it may run. A rejected statement is never sent to the database. This is defense in depth: the read-only transaction and PGOPTIONS the runner sets are the real enforcement.

type Structure

type Structure struct {
	Schema       string       `json:"schema"`
	Name         string       `json:"name"`
	Kind         string       `json:"kind"`
	RowEstimate  int64        `json:"row_estimate"`
	SizeBytes    int64        `json:"size_bytes"`
	Columns      []Column     `json:"columns"`
	Indexes      []Index      `json:"indexes"`
	ForeignKeys  []ForeignKey `json:"foreign_keys"`
	ReferencedBy []ForeignKey `json:"referenced_by"`
	DDL          string       `json:"ddl"`
}

Structure is the full detail of one table, what the schema outline leaves out. ForeignKeys point away from the table, ReferencedBy point at it.

type Table

type Table struct {
	Name        string   `json:"name"`
	Kind        string   `json:"kind"`
	RowEstimate int64    `json:"row_estimate"`
	SizeBytes   int64    `json:"size_bytes"`
	Columns     []Column `json:"columns"`
	Indexes     []Index  `json:"indexes"`
}

Table is a table, view, or other relation.

type Target

type Target struct {
	Exec        Execer
	ContainerID string
	Dialect     Dialect
	Limits      Limits
	// External marks a database this platform does not run: Exec starts a
	// helper that carries the connection, and ContainerID is unused.
	External bool
}

Target is one running database container.

func (Target) Evidence

func (t Target) Evidence(ctx context.Context) (Evidence, error)

Evidence counts schemas, tables and bytes straight from the catalog.

func (Target) Explain

func (t Target) Explain(ctx context.Context, st Statement, analyze bool) (Result, error)

Explain returns the plan for a read statement as text lines (Postgres, MySQL, and MariaDB all accept an EXPLAIN prefix).

func (Target) GetKey

func (t Target) GetKey(ctx context.Context, key string) (RedisValue, error)

GetKey reads one key's value with a read-only command chosen by type.

func (Target) Run

func (t Target) Run(ctx context.Context, st Statement, write bool) (Result, error)

Run executes a guard-approved statement. write must match the allowWrites value the statement was checked with.

func (Target) RunTrusted

func (t Target) RunTrusted(ctx context.Context, sql string) (Result, error)

RunTrusted executes SQL the control plane built itself (introspection, table pages). It still runs in a read-only transaction.

func (Target) ScanKeys

func (t Target) ScanKeys(ctx context.Context, cursor, pattern string, count int) (RedisScan, error)

ScanKeys runs one SCAN step and looks up each key's type and TTL.

func (Target) Schema

func (t Target) Schema(ctx context.Context) ([]SchemaNode, error)

Schema lists every user schema with its tables, columns, and indexes.

func (Target) SchemaOutline

func (t Target) SchemaOutline(ctx context.Context) ([]SchemaNode, error)

SchemaOutline lists every user schema with its tables, row estimates and sizes but no columns or indexes, so a database with thousands of tables stays a small document. Per-table detail comes from TableStructure.

func (Target) TablePage

func (t Target) TablePage(ctx context.Context, q PageQuery) (Page, error)

TablePage reads one page. Schema, table, and column names are checked against the database's own catalog before they are quoted into SQL.

func (Target) TableStructure

func (t Target) TableStructure(ctx context.Context, schema, table string) (Structure, error)

TableStructure returns the columns, indexes, foreign keys in both directions, and DDL of one table, or ErrNotFound.

Jump to

Keyboard shortcuts

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