Documentation
¶
Overview ¶
Package driver is TableX's database abstraction layer.
Everything that genuinely differs between SQL engines lives behind the Dialect interface: DSN building, identifier quoting, placeholder syntax, metadata introspection and a handful of capability flags. The generic query path (open pool, run query, scan rows, paginate) is implemented once on top of database/sql in this package, so handlers never import a concrete engine package. Adding a new engine means implementing Dialect and registering it — nothing else in the application changes.
See docs/database-drivers.md for the design rationale.
Index ¶
- Constants
- Variables
- func AddForeignKeyDDL(d Dialect, t TableRef, name string, cols []string, refTable string, ...) string
- func AssembleCreateTable(qualified string, colDefs, quotedPK []string) string
- func BaseTypeName(typ string) string
- func BasicColumnDef(d Dialect, c ColumnSpec) string
- func CheckColumnScope(g GrantSpec, privs []string, grant bool, columnPrivs []string) error
- func ChooseFrameDelimiter(body string) string
- func DefaultLimitClause(limit int, offset int64) string
- func DropColumnDDL(d Dialect, t TableRef, col string) string
- func FloorWarning(d Dialect, info ServerInfo) string
- func FormatCollationMarker(kind, name, collation string) string
- func FormatFrameMarker(delim string) string
- func GroupableDropClass(class string) bool
- func HasSelfEdge(name string, deps map[string][]string) bool
- func HasUnsafeIdentifierRune(s string) bool
- func HumanBytesIEC(n int64) string
- func IndexKeyParts(d Dialect, cols []IndexColumn) string
- func MemoizedDump(ctx context.Context, key string, load func() (any, error)) (any, error)
- func NextvalTextRefs(expr string) (refs []string, dynamic bool)
- func NonFiniteFloat(s string) string
- func NormalizePrivileges(privs []string, check func(string) bool) ([]string, error)
- func ParseFrameMarker(line string) (delim string, ok bool)
- func ParseQualifiedSeqLiteral(content string) (schema, name string, ok bool)
- func PrivilegeList(privs, cols []string, quote func(string) string) string
- func QuoteAnsiIdent(name string) string
- func QuoteAnsiString(s string) string
- func QuoteEach(d Dialect, names []string) []string
- func Register(d Dialect)
- func RegisteredNames() []string
- func RenameColumnDDL(d Dialect, t TableRef, old, newName string) string
- func RenderValueLiteral(quote func(string) string, hooks ValueLiteralHooks, col ResultColumn, v Value) string
- func RewriteSequenceRefs(ddl string, rewrites map[string]string) string
- func RoutineKeyword(r model.Routine) string
- func SCC(names []string, deps map[string][]string) [][]string
- func SeqRefKey(schema, name string) string
- func ServerFlavorFromContext(ctx context.Context) string
- func SplitAccount(s string) (user, host string)
- func StreamResult(rows *sql.Rows, perRow func(cols []ResultColumn, row []Value) error) error
- func StringSet(names []string) map[string]bool
- func TopoOrder(names []string, deps map[string][]string) []string
- func UnbracketHost(host string) string
- func ValidNewIdentifier(caps Capabilities, s string) bool
- func ValidPrivilegeKeyword(s string) bool
- func ValidateCreateTable(cols []ColumnSpec, pk []string) error
- func WithDumpMemo(ctx context.Context) context.Context
- func WithReadTimeout(ctx context.Context) (context.Context, context.CancelFunc)
- func WithServerFlavor(ctx context.Context, flavor string) context.Context
- func XHexLiteral(b []byte) string
- type BulkIntrospector
- type Capabilities
- type Collation
- type CollationLister
- type CollationProber
- type ColumnModifier
- type ColumnPlacement
- type ColumnPrivileger
- type ColumnRenamer
- type ColumnSpec
- type ConnParams
- type Connection
- func (c *Connection) AlterUser(ctx context.Context, u UserSpec) error
- func (c *Connection) AuditTeardown(ctx context.Context, planned []TeardownDrop) ([]string, error)
- func (c *Connection) Browse(ctx context.Context, t TableRef, page Pagination, sorts []Sort) (*ResultSet, error)
- func (c *Connection) BulkColumns(ctx context.Context, scope Scope) (map[string][]model.Column, bool, error)
- func (c *Connection) BulkForeignKeys(ctx context.Context, scope Scope) (map[string][]model.ForeignKey, bool, error)
- func (c *Connection) CanManageEvents() bool
- func (c *Connection) CanManagePrivileges() bool
- func (c *Connection) CanManageRoles() bool
- func (c *Connection) CanManageRoutines() bool
- func (c *Connection) CanManageTriggers() bool
- func (c *Connection) CanManageUsers() bool
- func (c *Connection) Capabilities() Capabilities
- func (c *Connection) Close() error
- func (c *Connection) ColumnGrantablePrivileges() []string
- func (c *Connection) Columns(ctx context.Context, t TableRef) ([]model.Column, error)
- func (c *Connection) CountRows(ctx context.Context, t TableRef) (int64, error)
- func (c *Connection) CountRowsBounded(ctx context.Context, t TableRef, max int64) (n int64, exact bool, err error)
- func (c *Connection) CreateProgram(ctx context.Context, create string) error
- func (c *Connection) CreateSQL(ctx context.Context, t TableRef) (string, error)
- func (c *Connection) CreateUser(ctx context.Context, u UserSpec) error
- func (c *Connection) DB() *sql.DB
- func (c *Connection) DataSelectOnly(ctx context.Context, scope Scope, tables []string) (map[string]bool, error)
- func (c *Connection) Dialect() Dialect
- func (c *Connection) DropEvent(ctx context.Context, s Scope, e model.Event) error
- func (c *Connection) DropRoutine(ctx context.Context, s Scope, r model.Routine) error
- func (c *Connection) DropTrigger(ctx context.Context, s Scope, t model.Trigger) error
- func (c *Connection) DropUser(ctx context.Context, name, host string) error
- func (c *Connection) DumpDataTables(ctx context.Context, scope Scope, tables []string) ([]string, error)
- func (c *Connection) DumpForeignTable(ctx context.Context, scope Scope, name string) (DumpPlan, error)
- func (c *Connection) DumpGlobalObjects(ctx context.Context, structure bool) (DumpPlan, error)
- func (c *Connection) DumpInheritsChildCreate(ctx context.Context, t TableRef, parents []string) (string, error)
- func (c *Connection) DumpObjects(ctx context.Context, scope Scope, tables []string, dbScope, structure bool) (DumpPlan, error)
- func (c *Connection) DumpTableCreate(ctx context.Context, t TableRef) (string, error)
- func (c *Connection) DumpTableCreateStaged(ctx context.Context, t TableRef, parents []string, ...) (string, []DumpScript, error)
- func (c *Connection) DumpView(ctx context.Context, scope Scope, name string, withData bool) (plan DumpPlan, ok bool, err error)
- func (c *Connection) Engine() string
- func (c *Connection) EstimateRows(ctx context.Context, t TableRef) (int64, error)
- func (c *Connection) Exec(ctx context.Context, query string) (ExecResult, error)
- func (c *Connection) ExecScript(ctx context.Context, stmts []string, useTx bool) error
- func (c *Connection) Explain(ctx context.Context, query string, analyze bool) (*ResultSet, error)
- func (c *Connection) ExplainSQL(query string, analyze bool) (string, bool)
- func (c *Connection) FloorWarning() string
- func (c *Connection) ForeignKeys(ctx context.Context, t TableRef) ([]model.ForeignKey, error)
- func (c *Connection) Grant(ctx context.Context, g GrantSpec) error
- func (c *Connection) GrantRole(ctx context.Context, g RoleGrant) error
- func (c *Connection) GrantRoutine(ctx context.Context, g RoutineGrant) error
- func (c *Connection) GrantablePrivileges(table bool) []string
- func (c *Connection) Indexes(ctx context.Context, t TableRef) ([]model.Index, error)
- func (c *Connection) Info() ServerInfo
- func (c *Connection) InheritanceParents(ctx context.Context, scope Scope, tables []string) (map[string][]string, error)
- func (c *Connection) IsForeignTable(ctx context.Context, scope Scope, name string) (bool, error)
- func (c *Connection) KillProcess(ctx context.Context, id int64) error
- func (c *Connection) ListCollations(ctx context.Context) ([]Collation, error)
- func (c *Connection) ListDatabaseNames(ctx context.Context) ([]model.Database, error)
- func (c *Connection) ListDatabases(ctx context.Context) ([]model.Database, error)
- func (c *Connection) ListEvents(ctx context.Context, scope Scope) ([]model.Event, error)
- func (c *Connection) ListRoutines(ctx context.Context, scope Scope) ([]model.Routine, error)
- func (c *Connection) ListSchemas(ctx context.Context, database string) ([]model.Schema, error)
- func (c *Connection) ListTableNames(ctx context.Context, scope Scope) ([]model.Table, error)
- func (c *Connection) ListTables(ctx context.Context, scope Scope) ([]model.Table, error)
- func (c *Connection) ListTriggers(ctx context.Context, scope Scope) ([]model.Trigger, error)
- func (c *Connection) ListUsers(ctx context.Context) ([]model.User, error)
- func (c *Connection) ListViews(ctx context.Context, scope Scope) ([]model.View, error)
- func (c *Connection) ObjectDefinition(ctx context.Context, scope Scope, kind ProgramKind, name string) (def string, ok bool, err error)
- func (c *Connection) Ping(ctx context.Context) error
- func (c *Connection) Privileges(ctx context.Context, ref TableRef) ([]model.Privilege, error)
- func (c *Connection) ProcessIDColumn() string
- func (c *Connection) QualifiedName(t TableRef) string
- func (c *Connection) Query(ctx context.Context, query string, limit int) (*ResultSet, error)
- func (c *Connection) ReplaceProgram(ctx context.Context, drop []string, create, restore string) error
- func (c *Connection) Revoke(ctx context.Context, g GrantSpec) error
- func (c *Connection) RevokeRole(ctx context.Context, g RoleGrant) error
- func (c *Connection) RevokeRoutine(ctx context.Context, g RoutineGrant) error
- func (c *Connection) RoleMemberships(ctx context.Context) ([]model.RoleMembership, error)
- func (c *Connection) RoutineGrantablePrivileges() []string
- func (c *Connection) RoutinePrivileges(ctx context.Context, s Scope, r model.Routine) ([]model.Privilege, error)
- func (c *Connection) RunTableMaintenance(ctx context.Context, t TableRef, op string) (*ResultSet, error)
- func (c *Connection) SearchExpr(col string) string
- func (c *Connection) SetStatementObserver(fn StatementObserver)
- func (c *Connection) Stream(ctx context.Context, query string, perRow func([]ResultColumn, []Value) error) error
- func (c *Connection) StreamArgs(ctx context.Context, query string, args []any, ...) error
- func (c *Connection) TableMaintenanceOps() []TableMaintenanceOp
- func (c *Connection) Tuning() Tuning
- func (c *Connection) ValueLiteral(col ResultColumn, v Value) string
- func (c *Connection) WithReadTimeout(ctx context.Context) (context.Context, context.CancelFunc)
- type DDLErrorHint
- type DataScoper
- type DatabaseManager
- type DatabaseRebinder
- type DefinitionViewer
- type Dialect
- type DropForm
- type DumpClause
- type DumpMarker
- type DumpMemo
- type DumpPlan
- type DumpScript
- type DumpTableNode
- type Dumper
- type EventManager
- type ExecResult
- type ExportConnAdjuster
- type FilePathValidator
- type ForeignKeyEditor
- type ForeignTableDumper
- type GlobalDumper
- type GrantSpec
- type Grouper
- type IndexColumn
- type IndexOptioner
- type IndexOptions
- type IndexSpec
- type Inheritor
- type LexerProfile
- type LoginDatabaseHint
- type LoginFormHinter
- type MaintenanceDatabaseLister
- type Monitor
- type NameLister
- type NestedGrouper
- type Pagination
- type ParamsNormalizer
- type Pinned
- func (p *Pinned) Close() error
- func (p *Pinned) Dialect() Dialect
- func (p *Pinned) Engine() string
- func (p *Pinned) Exec(ctx context.Context, query string) (ExecResult, error)
- func (p *Pinned) Explain(ctx context.Context, query string, analyze bool) (*ResultSet, error)
- func (p *Pinned) ExplainSQL(query string, analyze bool) (string, bool)
- func (p *Pinned) Query(ctx context.Context, query string, limit int) (*ResultSet, error)
- type PoolOpener
- type PrivilegeManager
- type Privileger
- type ProcessManager
- type ProgramKind
- type ResultColumn
- type ResultSet
- type ReturningCaps
- type RoleGrant
- type RoleManager
- type RoutineGrant
- type RoutineManager
- type RoutinePrivileger
- type RowEstimator
- type SchemaEditor
- type SchemaManager
- type Scope
- type SearchCaster
- type ServerDumpFramer
- type ServerDumpProfile
- type ServerInfo
- type ServerSpecializer
- type Sort
- type StagedTableDumper
- type StatementEvent
- type StatementLexer
- type StatementObserver
- type StorageDDL
- type StorageHost
- type TableMaintainer
- type TableMaintenanceOp
- type TableRef
- type TeardownAuditor
- type TeardownDrop
- type TriggerManager
- type Tuning
- type UserManager
- type UserSpec
- type Value
- type ValueListTyper
- type ValueLiteralHooks
- type VersionFloor
- type ViewDumper
Constants ¶
const ( ObjectTable = "table" ObjectView = "view" ObjectMatView = "matview" )
SchemaEditor is an optional Dialect capability for structure editing (DDL). Engines opt in by implementing it; handlers discover it via a type assertion, mirroring Monitor. Every identifier in the returned statements is quoted with QuoteIdent inside the builder — the handler validates identifiers first (new names via ValidNewIdentifier, existing objects by exact match against introspection). Builders return one or more statements: a PostgreSQL column "modify" expands to several ALTER COLUMN steps; other engines return a single statement. Referential actions passed to the FK builders are pre-validated keywords emitted verbatim. Object kinds for DropObjectSQL / RenameObjectSQL. matview is PostgreSQL-only; other engines treat it as a plain view.
const ( DefaultMaxOpenConns = 8 DefaultMaxIdleConns = 4 )
Defaults for a zero Tuning. Pool sizing is deliberately modest: PoolCap multiplies it, and the product is what has to stay clear of the server's own max_connections.
const MaxCellBytes = 1 << 20 // 1 MiB
MaxCellBytes bounds how much of one text cell a DISPLAY result retains. It mirrors the []byte branch's existing 1 MiB heuristic, which never covered the string branch — and pgx returns PostgreSQL text/json as string, so those cells were entirely uncapped: one 100 MB `text` value, times a 500-row page, is an unbounded allocation driven purely by row content.
Every display consumer renders at most a couple of hundred characters per cell (`truncate 200` in the browse and result grids), so 1 MiB is five thousand times more than any of them can show and the cap is never observable. The one consumer that needs the exact value — the row-edit prefill, which is submitted back as the new value — scans through ScanResultVerbatim instead, so a long TEXT column can never be truncated on save.
const ReadStmtTimeout = 60 * time.Second
ReadStmtTimeout is the DEFAULT budget for a single generated read statement (introspection, browse, count, estimate, and the three handler-built reads) so a few slow reads cannot tie up every pooled connection until the client disconnects. It is generous on purpose: the same introspection methods drive metadata pages and export planning on large schemas, which must not be truncated. Operators override it with Tuning.ReadStmtTimeout.
It is applied ONLY to generated reads. It is deliberately NOT applied to the SQL console (Query — the user's own SQL), export Stream, any Pinned path, or any generated mutation/DDL (Exec/ExecScript): force-cancelling a write or DDL mid-statement is riskier (partial effects, lock churn) than a slow read holding a connection, and those are already bounded by the request context.
Variables ¶
var ErrUnsupported = errors.New("operation not supported by this engine")
ErrUnsupported is returned by optional Dialect operations that a given engine does not provide (e.g. listing events on SQLite). Callers gate on Capabilities() before calling, so this is a defensive backstop.
Functions ¶
func AddForeignKeyDDL ¶
func AddForeignKeyDDL(d Dialect, t TableRef, name string, cols []string, refTable string, refCols []string, onUpdate, onDelete string) string
AddForeignKeyDDL builds the "ALTER TABLE … ADD CONSTRAINT … FOREIGN KEY …" statement shared verbatim by the MySQL and PostgreSQL SchemaEditors. The reference target carries both Database and Schema from t; each dialect's QualifyTable selects the level it qualifies by (MySQL: Database, PostgreSQL: Schema) and ignores the other, so v1's same-database / same-schema target assumption holds for both engines without an engine branch here.
func AssembleCreateTable ¶
AssembleCreateTable renders the CREATE TABLE statement every SchemaEditor shares: the pre-built column definitions plus, when quotedPK is non-empty, a PRIMARY KEY table constraint. qualified and quotedPK arrive already quoted (QualifyTable / QuoteEach); what genuinely diverges per engine — the columnDef grammar and PostgreSQL's COMMENT ON COLUMN trailer — stays with the dialects.
func BaseTypeName ¶
BaseTypeName extracts the lower-cased bare type name from a SQL column type ("DECIMAL(10,2)" → "decimal"). Shared by the dialects' column builders and introspection.
func BasicColumnDef ¶
func BasicColumnDef(d Dialect, c ColumnSpec) string
BasicColumnDef renders the simple column definition shared by the PostgreSQL and SQLite DDL builders: quoted name, type, NOT NULL, DEFAULT. (MySQL's columnDef carries extra attributes — unsigned/zerofill/charset/ auto-increment — and stays engine-local.) ColumnSpec.Default is already a validated SQL fragment (see the handlers' buildDefault), never raw input.
func CheckColumnScope ¶
CheckColumnScope re-checks a column-scoped spec inside a grant builder. The grammar rule — a column list needs a table, since the database-scope object takes none — applies to GRANT and REVOKE alike; the per-privilege rule runs on grant only, mirroring the allowlist asymmetry the builders already have (a revoke is authorized by the introspected grant, not by a curated list). columnPrivs is the engine's own column-grantable allowlist — the one per-engine fact in an otherwise identical check, and an intentional divergence, not a bug in flight.
func ChooseFrameDelimiter ¶
ChooseFrameDelimiter picks a DELIMITER token for an opaque frame around body: one that occurs nowhere in the body, so neither TableX's exact-line terminator match nor an external client's substring scan can fire early. The terminator is always emitted on its own line, so a body merely *ending* in a token prefix (e.g. '$' before a '$$' token) cannot splice into it.
func DefaultLimitClause ¶
DefaultLimitClause renders the "LIMIT n OFFSET n" clause every supported engine accepts; the dialects delegate here rather than each formatting it. The offset is int64 (see Dialect.LimitClause).
func DropColumnDDL ¶
DropColumnDDL builds the "ALTER TABLE … DROP COLUMN …" statement shared verbatim by all three SchemaEditors (unlike columnDef / AddIndexSQL, which genuinely diverge per engine). Each dialect's QualifyTable selects the level it qualifies by.
func FloorWarning ¶
func FloorWarning(d Dialect, info ServerInfo) string
FloorWarning returns a human-readable advisory when the connected server is older than the dialect's documented floor, or "" when it is new enough, the version could not be parsed, or the dialect declares no floor.
func FormatCollationMarker ¶
FormatCollationMarker renders the disclosure marker line for one object. The caller guarantees kind is a collationMarkerKinds member.
func FormatFrameMarker ¶
FormatFrameMarker renders the opaque-frame marker line for a frame using delim (chosen via ChooseFrameDelimiter, so it is always grammar-valid).
func GroupableDropClass ¶
GroupableDropClass reports whether class may be rendered as a multi-object DROP (see groupableDropClasses).
func HasSelfEdge ¶
HasSelfEdge reports whether deps[name] contains name itself — the one cycle shape SCC's single-member components cannot distinguish from an acyclic node.
func HasUnsafeIdentifierRune ¶
HasUnsafeIdentifierRune reports whether s contains a rune no identifier-shaped input may carry: control characters INCLUDING DEL (0x7f), quote characters, backtick, semicolon, or backslash. It is the single reject set behind ValidNewIdentifier, the handlers' account-name validator and safeSortColumn.
Unifying on one set was a deliberate hardening, not behavior-neutral: sort columns previously accepted DEL and now reject it (a column literally named with a 0x7f byte loses sortability — an acceptable trade for one shared, strictest-common reject set).
func HumanBytesIEC ¶
HumanBytesIEC formats a non-negative byte count using IEC units (B, KiB, MiB, …). It is the shared core; internal/view's HumanBytes wraps it with an em-dash guard for the negative "unknown" sentinel.
func IndexKeyParts ¶
func IndexKeyParts(d Dialect, cols []IndexColumn) string
IndexKeyParts renders an index's key list — "`a`(10), `b` DESC" — shared by the three SchemaEditors because the syntax genuinely coincides where the options exist at all.
It drops any part the dialect's IndexOptions does not claim, so this is the single place enforcing that promise rather than three. The handler refuses an unsupported option first, with a message; this is the backstop that keeps a caller who skipped that check from emitting SQL the engine cannot parse — or, worse, SQL it parses and ignores.
func MemoizedDump ¶
MemoizedDump returns the value for key, calling load on a miss. Errors are cached too: a failed catalog read would fail the same way for every table, so re-running it per table only multiplies the latency before the same failure.
key must identify everything load depends on — the schema at minimum.
func NextvalTextRefs ¶
NextvalTextRefs scans one deparsed default expression for LATE-BOUND sequence references — deparsed as nextval(('literal'::text)::regclass) — returning the literal contents, plus dynamic=true when any nextval argument is not a plain (possibly parenthesized) quoted literal: an expression or concatenation cannot be resolved or rewritten and the caller warns. Early-bound '…'::regclass arguments are ignored here — pg_depend already records those edges.
func NonFiniteFloat ¶
NonFiniteFloat classifies a numeric column's textual value as a non-finite float token — "nan", "+inf" or "-inf" (as strconv.FormatFloat emits them, plus PostgreSQL's Infinity spellings) — or "" for an ordinary finite value. A non-finite value rendered bare is not a valid unquoted SQL numeric literal and would fail on restore, so each dialect's ValueLiteral maps these to an engine-correct literal (or NULL when the engine cannot store them).
func NormalizePrivileges ¶
NormalizePrivileges trims and upper-cases submitted privilege keywords and re-validates each with check before a dialect emits them verbatim. Shared by the GrantSQL builders (check = allowlist membership) and RevokeSQL builders (check = ValidPrivilegeKeyword).
func ParseFrameMarker ¶
ParseFrameMarker validates line against the full frame marker grammar and returns the frame's delimiter token. ok is false for anything else.
func ParseQualifiedSeqLiteral ¶
ParseQualifiedSeqLiteral parses a late-bound literal's content into (schema, name). ok is false for an unqualified or malformed reference — binding one against an unknown restore-time search_path would be a guess.
func PrivilegeList ¶
PrivilegeList renders the privilege list of a GRANT or REVOKE. With no columns it is the bare comma-joined keywords; with columns each keyword carries the SAME parenthesised column list — "SELECT (a, b), UPDATE (a, b)" — which is the MySQL and PostgreSQL grammar both engines share, so the two dialects need not each re-derive it.
privs must already have passed NormalizePrivileges and cols must already have been matched against introspection; quote is the dialect's QuoteIdent. cols is emitted in the caller's order, which is the introspected column order, so two identical grants produce byte-identical SQL.
func QuoteAnsiIdent ¶
QuoteAnsiIdent quotes an identifier with the ANSI double-quote convention ("name", embedded quotes doubled), shared byte-for-byte by the PostgreSQL and SQLite dialects. QuoteAnsiString quotes a string literal ('lit', embedded single quotes doubled) the same way. Keeping one implementation guarantees the two engines cannot drift.
func QuoteAnsiString ¶
QuoteAnsiString quotes a string literal with the ANSI single-quote convention.
func QuoteEach ¶
QuoteEach returns names each quoted via the dialect's QuoteIdent, in order. Shared by the dialect DDL builders so the per-name quoting loop isn't reimplemented per engine.
func Register ¶
func Register(d Dialect)
Register adds a dialect to the global registry. Engine packages call this from their init(); cmd/tablex blank-imports them so registration happens at startup. Registering the same name twice panics (a programming error).
func RegisteredNames ¶
func RegisteredNames() []string
RegisteredNames returns every registered dialect's name, sorted. It exists so callers that only need to validate or display the engine list (config validation, error messages) do not have to hold Dialect values — and, more importantly, so no second copy of the list has to be maintained by hand.
The registry is populated by each engine package's init() via the blank imports in package main, so a build that omits a driver reports an engine list that matches what the binary can actually open.
func RenameColumnDDL ¶
RenameColumnDDL builds the "ALTER TABLE … RENAME COLUMN old TO new" statement, which all three engines spell identically (MySQL since 8.0.3, MariaDB since 10.5.2, PostgreSQL always, SQLite since 3.25). A dialect whose server may predate that support decides so in Capabilities, not here.
func RenderValueLiteral ¶
func RenderValueLiteral(quote func(string) string, hooks ValueLiteralHooks, col ResultColumn, v Value) string
RenderValueLiteral is the shared dump-literal scaffold behind every ValueLiteral implementation: NULL, binary, the optional text pre-pass, the numeric passthrough with non-finite handling, and the quoted-text fallback.
func RewriteSequenceRefs ¶
RewriteSequenceRefs replaces the content of every single-quoted literal in ddl that is cast to ::regclass or ::text AND parses as a schema-qualified identifier matching a rewrites key (SeqRefKey-shaped) with the mapped replacement (also SeqRefKey-shaped), rendered with PostgreSQL identifier quoting. Unqualified literals never match — a rewrite must never guess a search_path. A nil/empty map returns ddl unchanged.
func RoutineKeyword ¶
RoutineKeyword picks FUNCTION or PROCEDURE for a routine-scope grant statement. Both engines need the kind spelled out, each for its own reason: MySQL's grammar has no ON ROUTINE at all, so the wrong keyword targets an object that does not exist rather than failing to parse; PostgreSQL has ON ROUTINE from PG 11, but naming the kind keeps a mismatched Routine.Type an error here rather than a grant on whatever object shares the name.
func SCC ¶
SCC returns the strongly-connected components of the (names, deps) graph — Tarjan's algorithm, iterative-free depth-first (the graphs are small catalog object sets). deps[v] lists the nodes v depends on; edges to names outside the set are ignored (boundary edges). Components are returned in reverse topological order of the condensation (a component precedes the components that depend on it), each with its members in visit order. A single node is its own component; it is a GENUINE cycle only when len > 1 or it carries a self-edge — callers use SCC to find the cycles TopoOrder merely tolerates.
func SeqRefKey ¶
SeqRefKey builds the canonical lookup key for a schema-qualified sequence reference. NUL-separated so a dotted relation name cannot collide with a schema qualification.
func ServerFlavorFromContext ¶
ServerFlavorFromContext returns the flavor stashed by WithServerFlavor, or "".
func SplitAccount ¶
SplitAccount parses a "user@host" (or "'user'@'host'") account string on the LAST "@": the host is the substring after the final @, since user names may themselves contain @. Each part is trimmed of surrounding ' ` " quote characters (strings.Trim strips any run of them, not just one). This is the handler-facing account decoder, shared so handlers need not import a concrete dialect. It decodes the account <select> value (built from an already-decoded model.User Name@Host) and the server-reported CURRENT_USER() string — NEITHER of which uses information_schema's doubled-quote escaping. It therefore deliberately does NOT collapse doubled quotes; the mysql dialect's internal splitGrantee is the companion variant that DOES, because it decodes raw information_schema.*_PRIVILEGES grantees.
func StreamResult ¶
StreamResult iterates rows one at a time, calling perRow for each, without buffering the whole result set — used by streaming export. It closes rows.
func StringSet ¶
StringSet builds a membership set from a name slice — the "which tables are in scope" lookup every dialect's DumpObjects performs. Exported because the dialects live in separate packages.
func TopoOrder ¶
TopoOrder orders names so every dependency precedes its dependents: deps[v] lists the names v depends on. The sort is stable (ties keep the input order) and cycle-tolerant — a cycle's members are still all emitted (never dropped), in DFS-completion order rather than input order: for a 2-cycle A→B→A with input [A,B] the output is [B,A] (visiting A recurses into B, which completes first). Dialects use it to dependency-order view DDL, where a mutually-recursive cycle cannot be perfectly ordered but every member must still appear. The self-edge guard (dep != n) means a single node with a self-dependency is NOT reordered, so callers needing to detect a self-cycle must inspect the edges directly.
func UnbracketHost ¶
UnbracketHost returns a bracketed IP literal ("[::1]", "[fe80::1%eth0]") in bare form; any other host is returned unchanged. The network dialects call this before net.JoinHostPort, which brackets ANY host containing ':' — an already-bracketed input would otherwise come out double-bracketed ("[[::1]]:5432") and unresolvable. Boundary canonicalization (config.CanonicalHost) normally strips brackets before a host reaches a dialect; this is defense in depth for direct driver callers.
func ValidNewIdentifier ¶
func ValidNewIdentifier(caps Capabilities, s string) bool
ValidNewIdentifier reports whether s is acceptable as the name of an object being CREATED or RENAMED-to (table, database, column, index, constraint). It is deliberately permissive: names with spaces, hyphens, dots, leading digits or non-Latin scripts are legal in every supported engine and already browsable/editable in TableX — refusing to create them was an asymmetry, not a protection, since QuoteIdent makes them safe. It still rejects what could only be an injection attempt or an unusable name: empty/blank or space-padded names, control bytes, quote characters, backslashes, semicolons, and a length beyond the engine's byte limit (caps.IdentifierMaxBytes). The length rule is dialect-aware because PostgreSQL silently truncates a >63-byte identifier — validate-first prevents creating the object under a truncated name — while MySQL/SQLite would raise a clean error. Callers must still quote via QuoteIdent.
Do NOT use safeSortColumn for this: its contract assumes the name was already matched against live introspection, which a new name never is.
func ValidPrivilegeKeyword ¶
ValidPrivilegeKeyword reports whether s has the shape of a SQL privilege keyword: uppercase A–Z words separated by single spaces, at most 32 bytes ("SELECT", "ALL PRIVILEGES", "CREATE TEMPORARY TABLES"). It is the defence-in-depth gate on the introspection-driven revoke path (and on MySQL *_PRIVILEGES strings) — membership in the displayed grants or the grant allowlist is the primary check; this guarantees that whatever passed it can only ever be a keyword, never an injection fragment.
func ValidateCreateTable ¶
func ValidateCreateTable(cols []ColumnSpec, pk []string) error
ValidateCreateTable applies the CreateTableSQL contract checks shared by all engines: at least one column, no duplicate column names, and every PRIMARY KEY entry present among the columns.
func WithDumpMemo ¶
WithDumpMemo attaches a fresh memo to ctx. Call it once per dump, before the per-table builders run. The memo is scoped to that context and dies with it, so nothing is cached across requests and a catalog change between dumps cannot be served stale.
func WithReadTimeout ¶
WithReadTimeout derives a context bounded by the DEFAULT ReadStmtTimeout, for callers with no Connection in hand. Prefer (*Connection).WithReadTimeout, which honours the operator's configured budget.
func WithServerFlavor ¶
WithServerFlavor carries the connection's already-detected engine flavor (e.g. "MariaDB" vs "MySQL", parsed once by ServerInfo at connect) into a stateless Dialect introspection call, so the dialect need not re-query VERSION() on every call. ServerFlavorFromContext reads it back (empty when unset — e.g. a direct dialect call in a test, where the dialect falls back to its own probe).
func XHexLiteral ¶
XHexLiteral spells binary content as the X'…' hex literal MySQL, SQLite and the non-Dumper fallback share (PostgreSQL spells '\x…').
Types ¶
type BulkIntrospector ¶
type BulkIntrospector interface {
BulkColumns(ctx context.Context, db *sql.DB, scope Scope) (map[string][]model.Column, error)
BulkForeignKeys(ctx context.Context, db *sql.DB, scope Scope) (map[string][]model.ForeignKey, error)
}
BulkIntrospector is an optional Dialect capability: one catalog query returns the columns (and one the foreign keys) of EVERY table in a scope, replacing a per-table N+1 on schema-wide pages (designer) and export preflight. Engines whose catalog is inherently per-table (SQLite PRAGMA) omit it, and callers fall back to the per-table methods. Implementations MUST be built on the same query as the per-table method (a shared scan with an optional table filter) so the two paths cannot drift.
type Capabilities ¶
type Capabilities struct {
HasSchemas bool // PostgreSQL: true; MySQL/SQLite: false
HasUsers bool // privilege management
// HasForeignKeys / HasStoredRoutines / HasTriggers / HasEvents / HasViews:
// the engine has the object kind at all. The four with a Connection listing
// gate it (ListViews / ListRoutines / ListTriggers / ListEvents return an
// empty slice without a round-trip when false), so a dialect that lacks the
// kind does not have to implement a stub that queries for nothing.
HasForeignKeys bool
HasStoredRoutines bool
HasTriggers bool
HasEvents bool // MySQL/MariaDB scheduled events
HasViews bool
SupportsExplain bool
SupportsTransactionalDDL bool // PG: true; MySQL: false; SQLite: partial
SupportsCharset bool // CREATE DATABASE ... CHARACTER SET (MySQL)
SupportsColumnModify bool // ALTER COLUMN type/null/default (MySQL/PG; not SQLite)
SupportsColumnRename bool // ALTER TABLE ... RENAME COLUMN (all three; MariaDB needs 10.5.2+)
SupportsColumnPosition bool // FIRST / AFTER col placement on ADD and MODIFY (MySQL only)
SupportsForeignKeyDDL bool // ALTER TABLE add/drop FK (MySQL/PG; not SQLite)
CanManageDatabases bool // CREATE/DROP DATABASE (MySQL/PG; not SQLite — one file)
CanDropConnectedDatabase bool // DROP DATABASE works from a connection bound to that database (MySQL; PG needs a maintenance connection)
ExecReportsChangedRows bool // Exec's RowsAffected counts CHANGED rows, not matched (MySQL default protocol; PG/SQLite count matched)
SupportsTruncate bool // TRUNCATE TABLE (MySQL/PG; SQLite uses DELETE FROM)
AccountHasHost bool // account names carry a host part, 'user'@'host' (MySQL)
SupportsRoleAttributes bool // LOGIN/SUPERUSER/CREATEDB/CREATEROLE role attributes (PG)
// SupportsRoles: role membership (GRANT role TO account) is available, so
// the Users page renders its memberships section. Version-gated on
// MySQL/MariaDB and failing CLOSED on an unknown version — the catalog
// table is absent before MySQL 8.0 / MariaDB 10.0.5, so a wrong "yes" turns
// the page into an error rather than hiding a section. Promises RoleManager.
SupportsRoles bool
RestrictedDropColumn bool // dropping a PK/indexed/outgoing-FK column is refused (SQLite)
// IsNetworkEngine: the connection target is a network address (host/port)
// authenticated with credentials, rather than a local file the process
// opens. It is the login path's single engine discriminator and carries
// three consequences:
//
// - Ad-hoc login is offered ONLY for network engines. A file-backed
// engine has no credentials, so letting a visitor type its connection
// target would be an unauthenticated arbitrary file open (and, for
// SQLite historically, create) on the host. File-backed engines are
// reachable only through an operator-configured predefined server.
// - Credentials are collected at login only for network engines.
// - The SSRF host policy (auth.CheckHost / DialControl) applies only to
// network engines — there is no host to resolve otherwise.
//
// A future engine that is network-attached but should still be barred from
// ad-hoc login needs its own flag; today the two coincide exactly.
IsNetworkEngine bool
// ShowsSSLModeUI: the engine's AD-HOC LOGIN FORM exposes the sslmode
// selector (PG). Deliberately not "UsesSSLMode": MySQL also consumes and
// validates ConnParams.SSLMode from predefined-server config — the honest
// distinction is which login form shows the selector.
ShowsSSLModeUI bool
// server connection (SQLite: one file; ATTACH-ed names are session-scoped),
// so ConnFor never opens per-database pools.
DatabasesShareConnection bool
// IdentifierMaxBytes bounds a new/renamed object name in bytes (0 = no hard
// limit). PostgreSQL's 63 (NAMEDATALEN-1) is load-bearing: it silently
// truncates a longer identifier, creating the object under an unexpected
// name, so validate-first is a correctness requirement, not just politeness.
// MySQL's 64 and SQLite's absence surface over-long names as clean errors.
IdentifierMaxBytes int
}
Capabilities advertises which features an engine supports. The UI reads these and never assumes a feature exists, so tabs/links appear only where valid.
type Collation ¶
type Collation struct {
Name string
Charset string
Default bool // the charset's default collation
}
Collation is one server collation and the character set it belongs to, for the create-database form (offer) and its validation (allowlist).
type CollationLister ¶
type CollationLister interface {
ListCollations(ctx context.Context, db *sql.DB) ([]Collation, error)
}
CollationLister is an optional Dialect capability: engines whose CREATE DATABASE accepts a collation (Capabilities.SupportsCharset) implement it so the create-database form can offer — and the handler validate against — the server's real collation list (information_schema.COLLATIONS).
type CollationProber ¶
type CollationProber interface {
CollationProbeSQL() string
}
CollationProber is an optional Dialect capability backing the SQL import's db-collation marker verification: the single-row, two-column statement reporting the session's current database and that database's default collation. Engines whose dumps carry no collation markers omit it and the importer ignores marker events.
type ColumnModifier ¶
type ColumnModifier interface {
ModifyColumnSQL(t TableRef, old string, c ColumnSpec) ([]string, error)
}
ColumnModifier is the SchemaEditor half that changes an EXISTING column in place. It is separate because SQLite has no such statement at all — ALTER TABLE there can add, drop and rename a column but never redefine one — and folding it into SchemaEditor forced SQLite to carry a method that only ever returned an error, duplicating what Capabilities.SupportsColumnModify already says. An engine that sets that flag MUST implement this.
A PostgreSQL "modify" expands to several ordered ALTER COLUMN steps; other engines restate the whole column in one statement, which is why the caller fills the spec with the attributes the form cannot express first (see ColumnSpec).
type ColumnPlacement ¶
type ColumnPlacement int
ColumnPlacement says where a column sits in the table's column order. Only MySQL/MariaDB can express this (FIRST / AFTER col); PostgreSQL and SQLite have no reordering statement, so they report SupportsColumnPosition false and ignore it. The zero value keeps whatever the engine would do on its own: append for ADD COLUMN, leave the column where it is for a MODIFY.
const ( PlaceDefault ColumnPlacement = iota PlaceFirst PlaceAfter )
type ColumnPrivileger ¶
type ColumnPrivileger interface {
PrivilegeManager
// ColumnGrantablePrivileges is the subset of the table-scope allowlist that
// accepts a column list — SQL restricts it to the privileges that read or
// write column values (SELECT/INSERT/UPDATE/REFERENCES), and "GRANT DELETE
// (col)" is a syntax error rather than a narrower DELETE. It is the single
// source of truth for the form's column control and for grant validation,
// exactly as GrantablePrivileges is for the keyword set.
ColumnGrantablePrivileges() []string
}
ColumnPrivileger is an optional capability extending PrivilegeManager to column-scope grants (GRANT SELECT (a, b) ON t TO u). It embeds PrivilegeManager because column scope is a refinement of table scope, never an alternative to it: the same GrantSQL/RevokeSQL builders emit both, keyed on GrantSpec.Columns.
The read side needs no separate interface — Privileger returns column grants as ordinary model.Privilege rows carrying a Column, so an engine's grant listing and its revoke path stay one code path. Both halves must ship together: a grant nobody can list is a grant nobody can revoke.
type ColumnRenamer ¶
ColumnRenamer renames a column WITHOUT restating its definition. It is its own capability rather than a field on ColumnSpec because renaming and redefining do not travel together:
- SQLite implements no ColumnModifier at all — it cannot redefine a column — yet it has had ALTER TABLE … RENAME COLUMN since 3.25. Folding rename into the modify path would lock out the one engine whose only column edit IS a rename.
- MariaDB below 10.5.2 is the mirror image: it can redefine a column but has no RENAME COLUMN, which is why SupportsColumnRename is decided from the detected server version rather than inferred from SupportsColumnModify.
Keeping it separate also keeps a rename from being a redefinition by accident. MySQL's other spelling, CHANGE, restates the whole column, so a rename issued that way silently applies every attribute the form could not express; RENAME COLUMN touches the name and nothing else.
old is validated by the caller against introspection and newName via ValidNewIdentifier; implementations only quote and assemble. An engine that sets SupportsColumnRename MUST implement this.
type ColumnSpec ¶
type ColumnSpec struct {
Name string
Type string
Comment string
Nullable bool
Default *string
// DefaultExpr marks Default as an expression carried verbatim from
// introspection (CURRENT_TIMESTAMP, nextval(...), (uuid())), as opposed to a
// literal the form produced. MySQL wraps non-temporal expression defaults in
// the parentheses its grammar requires; without this flag an expression
// default re-applied through the editor would be re-quoted into a string
// literal ('CURRENT_TIMESTAMP').
DefaultExpr bool
// Attributes preserved across a whole-definition MODIFY (MySQL restates the
// entire column, so anything omitted is dropped). The handler fills these
// from the existing column before ModifyColumnSQL; the add path leaves them
// zero. Engines that modify per-attribute (PostgreSQL) ignore them, except
// Identity, which PostgreSQL needs to avoid DROP/SET DEFAULT on an identity
// column (a hard error).
AutoIncrement bool // MySQL AUTO_INCREMENT
Unsigned bool // MySQL UNSIGNED numeric attribute
Zerofill bool // MySQL ZEROFILL numeric attribute
OnUpdate string // MySQL "ON UPDATE <expr>" (validated CURRENT_TIMESTAMP[(n)])
Collation string // column collation to preserve (bare identifier)
Charset string // MySQL column character set to preserve (bare identifier)
Identity string // PostgreSQL identity mode to preserve: "" / "always" / "default"
// Placement moves the column within the table's column ORDER. Only engines
// that set Capabilities.SupportsColumnPosition act on it; the others MUST
// ignore it rather than approximate it — PostgreSQL and SQLite have no way
// to reorder columns at all, and the only faithful alternative (rebuild the
// table, copy the data, swap it in) is not something a column edit should
// do behind the user's back.
Placement ColumnPlacement
// PlacementAfter names the EXISTING column that Placement == PlaceAfter
// refers to. The caller validates it against introspection first, and must
// reject the column being modified naming itself.
PlacementAfter string
// SameBaseType is set on a MODIFY when the new base type matches the existing
// one (only length/precision/attributes changed). PostgreSQL uses it to omit
// the "USING col::type" cast: a same-base change needs no cast and, on a
// shrink that would lose data, errors cleanly instead of silently truncating.
// Other engines ignore it; the add path leaves it false.
SameBaseType bool
}
ColumnSpec describes a column to add or modify. Default is nil for "no default"; a non-nil *Default is a pre-validated literal or expression emitted verbatim (the handler validates it and, for string types, quotes it via QuoteString before populating this). Type is the assembled, already-validated engine type (an allowlisted base type plus an optional length/precision).
type ConnParams ¶
type ConnParams struct {
Host string
Socket string
Port int
User string
Password string
Database string // optional for MySQL; required for PostgreSQL
FilePath string // SQLite database file path
SSLMode string // engine-specific TLS preference (sslmode / tls)
Params map[string]string // extra driver params, merged last
// DialControl, when non-nil, is installed as the net.Dialer.Control hook on
// every TCP connection opened from these params. It re-validates the
// *resolved peer IP* of each connection (the SSRF / DNS-rebinding backstop:
// a rebinding DNS answer cannot slip past the pre-flight resolution because
// the address actually dialed is re-checked). It is set only for ad-hoc
// network logins; predefined servers are operator-trusted and carry no
// dial-time restriction. SQLite ignores it (no network).
DialControl func(network, address string, c syscall.RawConn) error
// OnStatement, when non-nil, is notified after each statement this
// connection runs — the audit trail's statement half. It rides ConnParams
// rather than being set on a Connection so that every DERIVED dial inherits
// it for free: the session stamps it once at login and the per-database
// pools, pinned script connections and export connections all carry it.
//
// Which statements reach it is deliberately asymmetric, and the asymmetry is
// the whole design: see StatementObserver.
OnStatement StatementObserver
// Tuning carries the operator's pool and statement-timeout settings down
// every dial. It rides ConnParams rather than a package-level variable so
// there is no shared mutable state and a test can dial with its own values:
// the session's base params are stamped once at login, and every derived
// dial (per-database pool, pinned script, export connection, maintenance
// connection) copies them for free. A zero Tuning means "driver defaults".
Tuning Tuning
}
ConnParams are the user-supplied connection inputs collected at login, translated by each Dialect into an engine-specific DSN.
type Connection ¶
type Connection struct {
// contains filtered or unexported fields
}
Connection is the engine-neutral handle handlers use. It wraps a *sql.DB pool plus its Dialect and cached ServerInfo, and exposes high-level operations that delegate the engine-specific bits to the Dialect. Handlers never touch a concrete driver package or build raw SQL for identifiers.
func Open ¶
func Open(ctx context.Context, d Dialect, p ConnParams) (*Connection, error)
Open dials a database using the dialect and params, verifies connectivity, loads ServerInfo and returns a ready Connection. Pool limits come from p.Tuning (operator config), defaulted so a single browser cannot exhaust the server.
func (*Connection) AlterUser ¶
func (c *Connection) AlterUser(ctx context.Context, u UserSpec) error
AlterUser sets an account's password and/or role attributes.
func (*Connection) AuditTeardown ¶
func (c *Connection) AuditTeardown(ctx context.Context, planned []TeardownDrop) ([]string, error)
AuditTeardown returns the drop-first teardown advisories (see TeardownAuditor); no warnings for a dialect without the capability.
func (*Connection) Browse ¶
func (c *Connection) Browse(ctx context.Context, t TableRef, page Pagination, sorts []Sort) (*ResultSet, error)
Browse runs a paginated, optionally sorted SELECT * over a table. Sort columns must already exist (validated by the caller against Columns); Browse re-checks their shape and quotes them. It never accepts a raw WHERE fragment — filtering is done through the parameterized Search path — so there is no string-concat injection surface here.
func (*Connection) BulkColumns ¶
func (c *Connection) BulkColumns(ctx context.Context, scope Scope) (map[string][]model.Column, bool, error)
BulkColumns returns every table's columns in one query when the dialect supports it (ok=false otherwise — the caller falls back to per-table calls).
func (*Connection) BulkForeignKeys ¶
func (c *Connection) BulkForeignKeys(ctx context.Context, scope Scope) (map[string][]model.ForeignKey, bool, error)
BulkForeignKeys returns every table's foreign keys in one query when the dialect supports it (ok=false otherwise).
func (*Connection) CanManageEvents ¶
func (c *Connection) CanManageEvents() bool
CanManageEvents reports whether scheduled events can be administered here.
func (*Connection) CanManagePrivileges ¶
func (c *Connection) CanManagePrivileges() bool
CanManagePrivileges reports whether the engine supports GRANT/REVOKE.
func (*Connection) CanManageRoles ¶
func (c *Connection) CanManageRoles() bool
CanManageRoles reports whether role membership can be read and edited here.
func (*Connection) CanManageRoutines ¶
func (c *Connection) CanManageRoutines() bool
CanManageRoutines reports whether stored routines can be administered here.
func (*Connection) CanManageTriggers ¶
func (c *Connection) CanManageTriggers() bool
CanManageTriggers reports whether triggers can be administered here.
func (*Connection) CanManageUsers ¶
func (c *Connection) CanManageUsers() bool
CanManageUsers reports whether the engine supports account administration.
func (*Connection) Capabilities ¶
func (c *Connection) Capabilities() Capabilities
Capabilities returns the engine capability flags.
func (*Connection) Close ¶
func (c *Connection) Close() error
Close shuts the pool down (called on logout / session expiry).
func (*Connection) ColumnGrantablePrivileges ¶
func (c *Connection) ColumnGrantablePrivileges() []string
ColumnGrantablePrivileges returns the privileges that accept a column list on this engine, or nil when column-scope grants are unsupported. A nil result is the signal the UI uses to hide the column control entirely.
func (*Connection) CountRows ¶
CountRows returns the exact row count for a table. Used to drive pagination totals.
func (*Connection) CountRowsBounded ¶
func (c *Connection) CountRowsBounded(ctx context.Context, t TableRef, max int64) (n int64, exact bool, err error)
CountRowsBounded counts a relation's rows but stops looking once more than max exist, so a relation with no usable statistics estimate — a VIEW above all, whose count re-runs the whole underlying query — cannot cost an unbounded scan on every Browse and Structure render.
It reports exact=true with the true count when the relation holds at most max rows, and exact=false with max when it holds more ("at least this many"). The engine work is bounded either way: the derived table stops at max+1 rows. max <= 0 degrades to the plain unbounded CountRows.
This is phpMyAdmin's $cfg['MaxExactCountViews'] shape. The SQL — SELECT COUNT(*) FROM (SELECT 1 FROM rel LIMIT n) t — is accepted verbatim by every supported engine; only the LIMIT spelling varies, and that comes from the dialect. The derived table is aliased because MySQL requires it.
func (*Connection) CreateProgram ¶
func (c *Connection) CreateProgram(ctx context.Context, create string) error
CreateProgram runs one stored-program CREATE statement. It is a plain Exec rather than a script: the statement is sent whole, so a body full of internal semicolons (MySQL's BEGIN…END) needs no DELIMITER — that is a client-side convention for text scripts, not a wire-protocol one.
func (*Connection) CreateUser ¶
func (c *Connection) CreateUser(ctx context.Context, u UserSpec) error
CreateUser creates a database account/role.
func (*Connection) DB ¶
func (c *Connection) DB() *sql.DB
DB exposes the underlying pool for advanced operations (transactions, batch import). Most code should prefer the higher-level methods.
func (*Connection) DataSelectOnly ¶
func (c *Connection) DataSelectOnly(ctx context.Context, scope Scope, tables []string) (map[string]bool, error)
DataSelectOnly returns the set of tables whose data SELECT must use FROM ONLY. A nil map (dialect without DataScoper, or MySQL/SQLite) means every table uses a plain FROM — today's behavior.
func (*Connection) Dialect ¶
func (c *Connection) Dialect() Dialect
Dialect returns the engine dialect (for quoting in DDL builders, etc.).
func (*Connection) DropEvent ¶
DropEvent drops a scheduled event the caller has already resolved from a listing.
func (*Connection) DropRoutine ¶
DropRoutine drops a stored procedure or function the caller has already resolved from a listing.
func (*Connection) DropTrigger ¶
DropTrigger drops a trigger the caller has already resolved from a listing.
func (*Connection) DropUser ¶
func (c *Connection) DropUser(ctx context.Context, name, host string) error
DropUser removes a database account/role.
func (*Connection) DumpDataTables ¶
func (*Connection) DumpForeignTable ¶
func (c *Connection) DumpForeignTable(ctx context.Context, scope Scope, name string) (DumpPlan, error)
DumpForeignTable builds a single foreign table's structure-only plan (see ForeignTableDumper).
func (*Connection) DumpGlobalObjects ¶
DumpGlobalObjects collects the database-global objects (see GlobalDumper). An empty plan for dialects without the capability.
func (*Connection) DumpInheritsChildCreate ¶
func (c *Connection) DumpInheritsChildCreate(ctx context.Context, t TableRef, parents []string) (string, error)
DumpInheritsChildCreate emits a linked INHERITS child's CREATE (see Inheritor). It is only called for a dialect that returned parents from InheritanceParents, so the capability is always present here.
func (*Connection) DumpObjects ¶
func (*Connection) DumpTableCreate ¶
func (*Connection) DumpTableCreateStaged ¶
func (c *Connection) DumpTableCreateStaged(ctx context.Context, t TableRef, parents []string, stripDefaults, stripConstraints []string) (string, []DumpScript, error)
DumpTableCreateStaged re-renders a table create with deferred clauses (see StagedTableDumper). ErrUnsupported for dialects without the capability — the resolver only calls it for dialects that emitted deferrable table edges.
func (*Connection) DumpView ¶
func (c *Connection) DumpView(ctx context.Context, scope Scope, name string, withData bool) (plan DumpPlan, ok bool, err error)
DumpView dumps a single view/matview when the dialect supports it (ok true). ok is false for a dialect without ViewDumper, letting the caller fall back to the table path.
func (*Connection) Engine ¶
func (c *Connection) Engine() string
Engine returns the dialect name ("mysql", "postgres", "sqlite"). MariaDB reports "mysql" — unlike Info().Engine, which carries the detected flavor.
func (*Connection) EstimateRows ¶
EstimateRows returns the engine's statistics-based row estimate for a table, or -1 when no usable estimate exists (engine keeps none, relation never analyzed, view). Callers fall back to CountRows on -1.
func (*Connection) Exec ¶
func (c *Connection) Exec(ctx context.Context, query string) (ExecResult, error)
Exec runs a non-row statement and reports affected rows.
func (*Connection) ExecScript ¶
ExecScript runs a sequence of non-row statements. When useTx is set it wraps them in a single transaction (all-or-nothing, mirroring the importer's BeginTx pattern); otherwise it runs them sequentially, stopping at the first error. The structure editor relies on this because some builders emit several statements (e.g. a PostgreSQL column modify) that must run as one unit on engines with transactional DDL — Connection.Exec has no transaction path.
func (*Connection) Explain ¶
Explain runs EXPLAIN (or EXPLAIN ANALYZE) for a query if the engine supports it, returning the plan as a result set.
func (*Connection) ExplainSQL ¶
func (c *Connection) ExplainSQL(query string, analyze bool) (string, bool)
ExplainSQL returns the exact statement Explain would execute (e.g. SQLite's "EXPLAIN QUERY PLAN …"), so the console can label the result with what really ran rather than a generic "EXPLAIN …".
func (*Connection) FloorWarning ¶
func (c *Connection) FloorWarning() string
FloorWarning returns the advisory for a server older than the documented engine floor, or "" when the server is new enough (or the engine declares no floor — SQLite is compiled in, so there is no server to be too old).
func (*Connection) ForeignKeys ¶
func (c *Connection) ForeignKeys(ctx context.Context, t TableRef) ([]model.ForeignKey, error)
func (*Connection) Grant ¶
func (c *Connection) Grant(ctx context.Context, g GrantSpec) error
Grant runs a GRANT built from g.
func (*Connection) GrantRole ¶
func (c *Connection) GrantRole(ctx context.Context, g RoleGrant) error
GrantRole runs GRANT <role> TO <account>.
func (*Connection) GrantRoutine ¶
func (c *Connection) GrantRoutine(ctx context.Context, g RoutineGrant) error
GrantRoutine runs a GRANT on a routine.
func (*Connection) GrantablePrivileges ¶
func (c *Connection) GrantablePrivileges(table bool) []string
GrantablePrivileges returns the engine's curated privilege allowlist for the grant form and grant validation, or nil when unsupported.
func (*Connection) Info ¶
func (c *Connection) Info() ServerInfo
Info returns the cached server info.
func (*Connection) InheritanceParents ¶
func (c *Connection) InheritanceParents(ctx context.Context, scope Scope, tables []string) (map[string][]string, error)
InheritanceParents returns the same-schema ordinary-inheritance parent map (see Inheritor). A nil map — dialect without Inheritor — means no table links, so every table dumps standalone (today's behavior).
func (*Connection) IsForeignTable ¶
IsForeignTable reports whether name is a foreign table (false for dialects without the capability).
func (*Connection) KillProcess ¶
func (c *Connection) KillProcess(ctx context.Context, id int64) error
KillProcess terminates a server session. The caller must have matched id against a fresh process list first — the engine decides whether the account is ALLOWED to kill it, but TableX still refuses to name a session nobody reported.
func (*Connection) ListCollations ¶
func (c *Connection) ListCollations(ctx context.Context) ([]Collation, error)
ListCollations returns the server's collation list for the create-database form and its validation, or nil for engines without collation support.
func (*Connection) ListDatabaseNames ¶
ListDatabaseNames is the cheap identity-only database listing (Name/IsSystem populated, no sizes or table counts) for navigation and existence checks. It falls back to the full listing when the dialect has no NameLister.
func (*Connection) ListDatabases ¶
func (*Connection) ListEvents ¶
func (*Connection) ListRoutines ¶
func (*Connection) ListSchemas ¶
func (*Connection) ListTableNames ¶
ListTableNames is the cheap identity-only table listing (Name/Schema/Type populated, no statistics) for navigation and existence checks. It falls back to the full listing when the dialect has no NameLister.
func (*Connection) ListTables ¶
func (*Connection) ListTriggers ¶
func (*Connection) ListViews ¶
ListViews is capability-gated HERE rather than in each dialect, as are the three extended listings after it. The Dialect contract already says an engine without the feature must return an empty slice; enforcing it centrally means a new dialect cannot forget, an engine that lacks the object kind pays no round-trip for it, and the Capabilities flags are load-bearing rather than decorative.
func (*Connection) ObjectDefinition ¶
func (c *Connection) ObjectDefinition(ctx context.Context, scope Scope, kind ProgramKind, name string) (def string, ok bool, err error)
ObjectDefinition returns the full CREATE statement for one already-listed object. ok is false when the engine has no DefinitionViewer, which is not an error: it means the listing's own Definition is already the complete statement and the caller should use that instead of paying a second query.
func (*Connection) Ping ¶
func (c *Connection) Ping(ctx context.Context) error
Ping verifies the pool is still alive.
func (*Connection) Privileges ¶
Privileges lists grants for a database (ref.Table == "") or a table. Returns nil for engines without a privilege system (SQLite).
func (*Connection) ProcessIDColumn ¶
func (c *Connection) ProcessIDColumn() string
ProcessIDColumn returns the Processes() column holding the killable session identifier, or "" when the engine cannot terminate sessions.
func (*Connection) QualifiedName ¶
func (c *Connection) QualifiedName(t TableRef) string
QualifiedName exposes the engine-correct quoted table reference for handlers that build display SQL or pass-through queries.
func (*Connection) Query ¶
Query runs a row-returning statement and scans up to limit rows. Used by the SQL console (the user's own SQL under their own credentials) and by Browse.
func (*Connection) ReplaceProgram ¶
func (c *Connection) ReplaceProgram(ctx context.Context, drop []string, create, restore string) error
ReplaceProgram redefines an existing stored program: drop, then create.
On an engine with transactional DDL the pair runs in one transaction, so a rejected CREATE leaves the original untouched. MySQL has no such thing — every DDL statement commits itself — so a CREATE that fails after the DROP succeeded would destroy the routine outright. There, the object's previous definition is replayed to put it back, and the caller is told whether that worked: losing a stored procedure to a typo is not an acceptable outcome of pressing Save.
restore is the object's current full CREATE statement (what the editor was pre-filled with).
func (*Connection) Revoke ¶
func (c *Connection) Revoke(ctx context.Context, g GrantSpec) error
Revoke runs a REVOKE built from g.
func (*Connection) RevokeRole ¶
func (c *Connection) RevokeRole(ctx context.Context, g RoleGrant) error
RevokeRole runs REVOKE <role> FROM <account>.
func (*Connection) RevokeRoutine ¶
func (c *Connection) RevokeRoutine(ctx context.Context, g RoutineGrant) error
RevokeRoutine runs a REVOKE on a routine.
func (*Connection) RoleMemberships ¶
func (c *Connection) RoleMemberships(ctx context.Context) ([]model.RoleMembership, error)
RoleMemberships lists the role→account edges, or nil when unsupported (not an error: the section simply does not render).
func (*Connection) RoutineGrantablePrivileges ¶
func (c *Connection) RoutineGrantablePrivileges() []string
RoutineGrantablePrivileges returns the routine-scope allowlist, or nil when the engine has no routine grants — the signal the UI uses to hide the tab.
func (*Connection) RoutinePrivileges ¶
func (c *Connection) RoutinePrivileges(ctx context.Context, s Scope, r model.Routine) ([]model.Privilege, error)
RoutinePrivileges lists the grants on one routine, or nil when unsupported.
func (*Connection) RunTableMaintenance ¶
func (c *Connection) RunTableMaintenance(ctx context.Context, t TableRef, op string) (*ResultSet, error)
RunTableMaintenance runs one maintenance command and returns whatever the engine reported. op must be one of TableMaintenanceOps' names — the dialect rejects anything else, so an op cannot be smuggled in through the form.
It runs as a query, not an exec: MySQL's OPTIMIZE/CHECK/REPAIR/ANALYZE all answer with a status table that is the entire point of running them, while PostgreSQL's VACUUM and REINDEX simply return no rows. No read timeout is applied — VACUUM FULL on a large table legitimately takes minutes, and the request context already bounds it.
func (*Connection) SearchExpr ¶
func (c *Connection) SearchExpr(col string) string
SearchExpr returns the expression used to LIKE-match a column (already validated against introspection by the caller): the quoted identifier, text-cast where the engine requires it for LIKE (PostgreSQL).
func (*Connection) SetStatementObserver ¶
func (c *Connection) SetStatementObserver(fn StatementObserver)
SetStatementObserver installs the audit observer on an already-open connection.
It exists for one case: the LOGIN pool is dialled before the identity to record is known — it is the connection that reveals it — so its observer cannot ride the ConnParams the way every derived dial's does. Call it before the Connection is shared with anything; there is no locking, because at that point there is nothing to race.
func (*Connection) Stream ¶
func (c *Connection) Stream(ctx context.Context, query string, perRow func([]ResultColumn, []Value) error) error
Stream runs a query and delivers rows one at a time to perRow without buffering — used by streaming export of large tables.
func (*Connection) StreamArgs ¶
func (c *Connection) StreamArgs(ctx context.Context, query string, args []any, perRow func([]ResultColumn, []Value) error) error
StreamArgs is Stream with bound parameters, for an export restricted to specific rows: the row-identity values are BOUND, never concatenated, so a "with selected" export keeps the same injection-free guarantee as the edit and delete paths it shares its row keys with.
func (*Connection) TableMaintenanceOps ¶
func (c *Connection) TableMaintenanceOps() []TableMaintenanceOp
TableMaintenanceOps lists the maintenance commands this engine offers, or nil when it offers none.
func (*Connection) Tuning ¶
func (c *Connection) Tuning() Tuning
Tuning returns the resolved pool and timeout settings in force for this connection (no zero fields), for diagnostics and tests.
func (*Connection) ValueLiteral ¶
func (c *Connection) ValueLiteral(col ResultColumn, v Value) string
ValueLiteral renders a cell through the dialect's Dumper. The fallback for a non-Dumper dialect covers the generic shapes (NULL, X'…' hex, bare numerics with the finite guard, quoted text) so the data phase still emits something restorable.
func (*Connection) WithReadTimeout ¶
func (c *Connection) WithReadTimeout(ctx context.Context) (context.Context, context.CancelFunc)
WithReadTimeout derives a context bounded by this connection's read budget, for a generated read built OUTSIDE the Connection wrappers — the handlers (TableSearch, runQBE, fetchRow) that bind args through conn.DB().QueryContext and so cannot use Query (which takes no args). The caller must invoke the returned cancel once the rows are scanned.
type DDLErrorHint ¶
DDLErrorHint is an optional Dialect capability that turns a raw DDL error into a user-facing hint (e.g. PostgreSQL's "cannot alter type of a column used by a view"), so handlers surface guidance without sniffing engine-specific error text themselves. ok is false when the error has no engine-specific hint.
type DataScoper ¶
type DataScoper interface {
DataSelectOnly(ctx context.Context, db *sql.DB, scope Scope, tables []string) (map[string]bool, error)
}
DataScoper is an optional Dialect capability reporting, per table, whether its data SELECT must use `FROM ONLY` instead of `FROM`. PostgreSQL returns true for an ordinary relkind='r' table so an INHERITS parent scans only its OWN rows — its separately-dumped children then contribute their rows exactly once, instead of every child row being duplicated inside the parent (silent data corruption). A partitioned parent (relkind='p') returns false: its descendant rows ride the parent scan while the children are excluded from the data list.
type DatabaseManager ¶
type DatabaseManager interface {
// CreateDatabaseSQL creates the database. collation, when non-empty, is a
// default collation for the new database (MySQL/MariaDB `COLLATE …`; the
// charset is implied by the collation). It is emitted as a BARE identifier
// (backtick-quoting is invalid in that position), so the handler MUST have
// validated it against the server's introspected collation list
// (CollationLister) — never pass raw user input. Engines whose Capabilities
// lack SupportsCharset ignore it.
CreateDatabaseSQL(name, collation string) string
// DropDatabaseSQL drops the database. PostgreSQL emits WITH (FORCE) (13+) so
// the drop terminates OTHER sessions still connected to it rather than
// failing; it cannot drop the database its own session is connected to, so
// the caller must run it on a connection bound elsewhere.
DropDatabaseSQL(name string) string
}
DatabaseManager is an optional Dialect capability for server-level database administration (CREATE / DROP DATABASE). Engines whose Capabilities report CanManageDatabases implement it; SQLite (one file per connection) does not. The name is quoted with QuoteIdent inside the builder; the handler validates it (ValidNewIdentifier for create, introspection match for drop) first.
type DatabaseRebinder ¶
type DatabaseRebinder interface {
RebindDatabase(p ConnParams, database string) ConnParams
}
DatabaseRebinder is an optional Dialect capability for engines whose logical "database" does not map to a DSN parameter: SQLite's database is the file the connection opened (ATTACH names are session-scoped), so rebinding is the identity. Engines without the hook get the default p.Database = database rebind on the private-connection dial paths (PinnedFor / ExportConnFor / server-dump sections).
type DefinitionViewer ¶
type DefinitionViewer interface {
ObjectDefinition(ctx context.Context, db *sql.DB, scope Scope, kind ProgramKind, name string) (string, error)
}
DefinitionViewer is an optional Dialect capability returning the full, replayable CREATE statement for ONE named object.
It exists because model.Routine/Trigger/Event.Definition holds whatever the engine's catalog cheaply reports alongside the listing, and that is not the same artefact on every engine. PostgreSQL's pg_get_functiondef and pg_get_triggerdef, and SQLite's sqlite_master.sql, are already complete CREATE statements. MySQL's information_schema ROUTINE_DEFINITION/ACTION_STATEMENT is the BODY only — no signature, no parameter list, no DEFINER — which is why the dump path reads SHOW CREATE instead (see the mysql DumpObjects comment). MySQL therefore implements this; engines whose listed Definition is already complete omit it and Connection.ObjectDefinition reports "not supported" so the caller falls back to the listing, paying no second round-trip.
name must ALREADY have been validated against a listing from this same scope — implementations quote it, they do not check that it exists.
type Dialect ¶
type Dialect interface {
// Identity.
Name() string // canonical id: "mysql", "postgres", "sqlite"
DisplayName() string // human label: "MySQL / MariaDB", "PostgreSQL", "SQLite"
DefaultPort() int // 0 for engines without a network port (SQLite)
// Connection.
SQLDriverName() string // database/sql driver name: "mysql", "pgx", "sqlite"
BuildDSN(p ConnParams) (string, error) // engine-specific DSN/connection string
// SQL syntax.
QuoteIdent(name string) string // `name` / "name" with correct escaping
QuoteString(s string) string // 'literal' with correct escaping (for generated DDL/dumps)
Placeholder(n int) string // "?" (n ignored) / "$1"
// LimitClause renders "LIMIT x OFFSET y" (or the engine equivalent). The
// offset is int64 end-to-end: browse positions on >2^31-row tables must
// not truncate through a 32-bit int.
LimitClause(limit int, offset int64) string
QualifyTable(t TableRef) string // engine-correct "[db|schema.]table", quoted
// InsertDefaultRowSQL renders an INSERT that adds one all-defaults row, for a
// table with NO insertable (non-generated) columns — a zero-column table, or
// one whose every column is generated (both legal in PostgreSQL). Without it a
// SQL dump silently loses every such row. PostgreSQL/SQLite use DEFAULT VALUES;
// MySQL uses the empty () VALUES () form. qualified is already quoted.
InsertDefaultRowSQL(qualified string) string
// Connection-scoped helpers.
ServerInfo(ctx context.Context, db *sql.DB) (ServerInfo, error)
// Introspection — each takes ctx + *sql.DB and returns neutral model types.
ListDatabases(ctx context.Context, db *sql.DB) ([]model.Database, error)
ListSchemas(ctx context.Context, db *sql.DB, database string) ([]model.Schema, error)
ListTables(ctx context.Context, db *sql.DB, scope Scope) ([]model.Table, error)
Columns(ctx context.Context, db *sql.DB, t TableRef) ([]model.Column, error)
Indexes(ctx context.Context, db *sql.DB, t TableRef) ([]model.Index, error)
ForeignKeys(ctx context.Context, db *sql.DB, t TableRef) ([]model.ForeignKey, error)
CreateSQL(ctx context.Context, db *sql.DB, t TableRef) (string, error)
// Extended introspection. Implementations whose Capabilities report the
// feature absent should return an empty slice (not an error).
ListViews(ctx context.Context, db *sql.DB, scope Scope) ([]model.View, error)
ListRoutines(ctx context.Context, db *sql.DB, scope Scope) ([]model.Routine, error)
ListTriggers(ctx context.Context, db *sql.DB, scope Scope) ([]model.Trigger, error)
ListEvents(ctx context.Context, db *sql.DB, scope Scope) ([]model.Event, error)
ListUsers(ctx context.Context, db *sql.DB) ([]model.User, error)
// ExplainSQL wraps a query for EXPLAIN; ok is false when unsupported.
ExplainSQL(query string, analyze bool) (sql string, ok bool)
Capabilities() Capabilities
}
Dialect captures everything that differs between database engines. A single value per engine is registered at init time; it is stateless and safe for concurrent use (all per-connection state lives in *sql.DB / Connection).
func All ¶
func All() []Dialect
All returns every registered dialect, sorted by Name for stable UI ordering.
func Specialize ¶
func Specialize(d Dialect, info ServerInfo) Dialect
Specialize applies the dialect's optional ServerSpecializer hook, returning d unchanged when the engine has no per-connection state. It is the single specialization point shared by Open and OpenPinned so the pooled and pinned paths cannot drift apart.
type DropForm ¶
type DropForm struct {
// Class is the DROP command class whose grammar accepts a comma-separated
// object list ("FUNCTION", "TYPE", …). "" — the zero value — means the
// object's DROP has single-object syntax only (COLLATION, CAST, OPERATOR
// CLASS, USER MAPPING) and can never be grouped: such a cycle is RETAINED
// (drops omitted + warned) instead.
Class string
// Ref is this object's reference inside such a list: everything the command
// needs after the class keyword and IF EXISTS.
Ref string
// RoutineRef, when non-empty, is this object's reference inside a grouped
// DROP ROUTINE — the FLAT input signature (IN/INOUT/VARIADIC types only, no
// ORDER BY split), which is the one spelling that matches functions,
// procedures and aggregates alike, so an all-routine cycle of mixed kinds
// drops in ONE statement. It is NOT interchangeable with Ref: an ordered-set
// aggregate's identity arguments render an `ORDER BY` split that only DROP
// AGGREGATE accepts.
RoutineRef string
}
DropForm describes how one object's teardown DROP may be merged into a MULTI-OBJECT drop command. A dependency cycle on the RESTORED catalog (a mutually-recursive routine pair, a base type and its I/O functions) cannot be linearized into individual reverse-ordered DROPs — each one fails under RESTRICT while the other member still references it, and the importer stops at the first error. A single DROP command listing every member drops them as a group, so mutual dependencies AMONG them do not trigger RESTRICT (exactly like `DROP TABLE a, b` for circular foreign keys). CASCADE is never used: it would strip objects outside the export's knowledge.
type DumpClause ¶
type DumpClause struct {
Text string // appended to the script's SQL when the clause is kept
Deps []string // node ids this clause (and only this clause) requires
Finalize []DumpScript // the deferred DDL emitted instead when the clause is cut
PreData bool // true: Finalize runs in the pre-data finalizer lane (domain defaults/constraints must precede consuming types/tables and data); false: post-data
}
DumpClause is one deferrable trailing clause of a DumpScript (see DumpScript.Clauses). PostgreSQL's clause grammar composes by appending, so keeping a clause is exactly `SQL += Text` in declaration order.
type DumpMarker ¶
type DumpMarker struct {
Kind string // "routine", "trigger" or "event"
Name string // object name, decoded
Collation string // the source database's collation as recorded at dump time
}
DumpMarker is one validated TableX db-collation disclosure marker.
func ParseCollationMarker ¶
func ParseCollationMarker(line string) (DumpMarker, bool)
ParseCollationMarker validates line (without its trailing newline) against the full db-collation marker grammar and decodes it. ok is false for anything else — including marker-like lines with unknown types, malformed fields or trailing garbage, which stay ordinary comments.
type DumpMemo ¶
type DumpMemo struct {
// contains filtered or unexported fields
}
DumpMemo caches a dump's schema-wide catalog reads so a dialect's per-table builders can share one query instead of repeating it per table.
The problem it solves is structural: the dump planner calls DumpTableCreate once per table, and a dialect's builder may need facts (PostgreSQL identity options, for one) that are cheapest to read for the whole schema at once. The builder cannot see the table list, and the planner cannot see what the builder needs — so the memo rides the context between them, next to WithServerFlavor.
The contract is deliberately weak: a MISS simply computes the value. With no memo attached — every caller outside the dump path — every lookup misses, so the memoized and unmemoized paths return identical results and the memo can only change how many queries run, never what they produce. That is what keeps this an optimization rather than a second code path with its own bugs.
The load function runs under the memo's lock, so a value is computed exactly once even though the dump path is sequential today.
type DumpPlan ¶
type DumpPlan struct {
// Types are user-defined types (PostgreSQL enums, domains, composites)
// emitted BEFORE routines and tables, which may reference them — without
// this pass a restore into an empty server fails at the first CREATE TABLE
// naming an enum/domain. Their Drops run in teardown AFTER the tables, so a
// column using the type is already gone. Only PostgreSQL populates this, and
// the writer emits it only when structure is dumped.
Types []DumpScript
// Collations are user-defined collations (PostgreSQL) emitted BEFORE the types
// and tables — a domain, composite attribute or column COLLATE may reference
// one. Their Drops run in teardown AFTER the types (a domain over a collated
// base type drops first). Only PostgreSQL populates this, structure-gated.
Collations []DumpScript
// Routines are emitted before the tables: functions/procedures may be
// referenced by views, triggers or generated columns.
Routines []DumpScript
// Sequences (CREATE SEQUENCE) are emitted after teardown, before the
// tables, so a serial/standalone column DEFAULT nextval(...) resolves at
// CREATE TABLE time. Their Drops run in teardown AFTER the DROP TABLEs:
// an owned sequence is auto-dropped with its table, and an OWNED BY /
// default dependency would block an earlier DROP. Only PostgreSQL populates
// this; the writer emits it only when structure is dumped.
Sequences []DumpScript
// ForeignData (C4.3) holds foreign-table creates (schema plans) and rides
// the pre-data slot between sequences and tables (pg_dump's foreign-data
// priority; a foreign table can INHERIT a local parent — the edge hoists
// the parent). Structure-only: foreign tables have NO data pass — their
// rows live on the remote server.
ForeignData []DumpScript
// Views (and materialized views) are emitted after the tables,
// dependency-ordered so a view referencing another view restores.
Views []DumpScript
// PostData is emitted after all rows: foreign-key ALTERs (PostgreSQL),
// NOT VALID constraints, secondary indexes (SQLite), triggers (so restored
// INSERTs do not fire them), events, materialized-view refreshes and
// sequence synchronization, in that order.
PostData []DumpScript
// Warnings are best-effort advisories (skipped objects, unsynchronized
// external state, dependency hazards) the writer emits as inert `-- WARNING:`
// comment lines — routed through commentSafe — at the top of the dump body in
// every mode. They never fail the export.
Warnings []string
// SchemaComment is the COMMENT ON SCHEMA text (PostgreSQL), emitted in the
// structure phase INDEPENDENTLY of the CREATE SCHEMA line (the default/public
// section writes no CREATE SCHEMA, but a comment on it must still round-trip).
SchemaComment string
// ViewEdges (C3) are the DATABASE-WIDE (dependent, source) edges among
// views/matviews, as qualified "schema.name" pairs resolved by relation OID
// through pg_rewrite — the planner topo-orders cross-schema matview
// REFRESHes with them. Only PostgreSQL populates this.
ViewEdges [][2]string
// TableNodes (C3) maps each dumped table's BARE name to its dependency-graph
// metadata (tables are not DumpScripts — the handler owns their emission —
// so their edges ride here). Only PostgreSQL populates this.
TableNodes map[string]DumpTableNode
// DataOnlyTables (C4.3) names tables whose ROWS are dumped but whose
// STRUCTURE is not: the local leaves of a partition tree containing a
// FOREIGN leaf. The tree's root emits every child's structure recursively,
// but its data scan cannot run (a plain FROM on the root would query the
// foreign leaf's REMOTE server), so the local leaves are read individually
// (FROM ONLY) instead — emitting their creates too would duplicate the DDL.
DataOnlyTables []string
// SuppressedRelations (C4.3) names relations classified state (c) —
// unreproducible under the redaction policy, emitted only as inert
// templates — whose dependents (triggers/comments/rules) were suppressed
// with them.
SuppressedRelations []string
// StructureOnlyTables (C4.3) names tables whose STRUCTURE is dumped but
// whose DATA scan must not run: a mixed local/foreign partition tree's
// ROOT (its plain FROM would recurse into the foreign leaf and query the
// remote server; the local leaves in DataOnlyTables carry the rows).
StructureOnlyTables []string
// SequenceRewrites (C5.1/C5.2) maps an out-of-scope source sequence (keyed
// SeqRefKey(schema, name)) to the replacement sequence materialized in its
// place (a SeqRefKey-shaped value). The handler routes every emitted table's
// DDL through RewriteSequenceRefs so early-bound ('s'::regclass) and
// resolvable late-bound ('s'::text) default references bind to the
// replacement. Only PostgreSQL populates this, and only for scoped
// (non-database) exports — a database-scope dump emits every sequence.
SequenceRewrites map[string]string
// StagedDefaultColumns (C5.3) names, per BARE table, columns whose inline
// DEFAULT must be suppressed at CREATE time and re-established post-data
// (Kind "staged-default"): the multi-parent divergent-default conflict —
// CREATE … INHERITS fails on conflicting parent defaults before any staged
// DDL could run, so every affected hierarchy member creates default-less
// and re-emits its own state via ALTER TABLE ONLY. The handler renders
// these tables through StagedTableDumper. Only PostgreSQL populates this.
StagedDefaultColumns map[string][]string
}
DumpPlan is the non-table DDL a restore-equivalent SQL dump emits around the table CREATEs and data. Each slice is already ordered for restore.
type DumpScript ¶
type DumpScript struct {
// Kind classifies the object: "routine", "view", "matview", "trigger",
// "event", "index", "constraint", "sequence", "sequence-def",
// "sequence-own", "refresh". The exporter uses it for teardown ordering
// (constraints drop before tables) and to keep the data-section items
// (setval "sequence" / matview "refresh") in data-only dumps while dropping
// structure-only items ("sequence-def" CREATE SEQUENCE lives in DumpPlan.
// Sequences; "sequence-own" ALTER SEQUENCE … OWNED BY is post-data structure).
Kind string
Comment string // short human label for the dump ("Trigger trg_audit")
Drop string // optional teardown statement (DROP ... IF EXISTS), emitted when the user asked to drop first
SQL string
NeedsDelimiter bool // MySQL routine/trigger/event body: internal ';' needs DELIMITER wrapping
// DropInline marks an object whose Drop is emitted INLINE, immediately
// before its own CREATE (mysqldump parity for routines and events), instead
// of in the reverse-ordered teardown block. PostgreSQL leaves it false for
// every class: a routine's drop must precede the DROP TYPE of a type in its
// signature, which only the reverse teardown order guarantees.
DropInline bool
// StageOf, on a script that is one CREATION STAGE of a larger logical object
// (a shell type and the CREATE that completes it), is that object's
// canonical node id. Creation ordering uses the per-stage Name — the whole
// point of staging is that the stages sort separately — but TEARDOWN
// collapses them back onto this id: the RESTORED catalog holds one object
// whose dependencies close the very cycle staging broke, so the drop graph
// must see it as one node.
StageOf string
// DropForm describes how this object's Drop may be merged into a
// multi-object DROP — the only non-CASCADE way to drop a dependency cycle
// (see DropForm). Zero value: ungroupable.
DropForm DropForm
// Name is this script's node id in the C3 dump dependency graph:
// "<catalog-kind>:<qualified name>[(identity args)]" — e.g.
// "type:public.mood", "relation:public.emp", "routine:public.f(integer)".
// Identity arguments / operand types / access method are part of the id so
// overloads and cross-type members never collapse. A staged object's later
// stage appends "-final" ("type-final:…", "routine…-final"). Empty means the
// script takes no part in graph ordering (MySQL/SQLite plans) and keeps its
// stable class-priority position.
Name string
// DependsOn lists node ids this script requires created first. An id
// missing from the emitted set is a BOUNDARY edge (a reference to an
// out-of-scope object): the topological sort ignores it and the planner
// surfaces it as a restore-prerequisite diagnostic, never a failure.
DependsOn []string
// Clauses are deferrable trailing clauses (a domain's DEFAULT / CHECK):
// normally each Text is appended to SQL in order, but when a clause's Deps
// close a dependency cycle the planner CUTS it — the clause text is omitted
// and its Finalize scripts (ALTER … SET DEFAULT / ADD CONSTRAINT, plus any
// moved COMMENT) run later in the lane PreData selects.
Clauses []DumpClause
// Stub, when non-empty, is a dependency-reduced replacement create for a
// routine caught in a dependency cycle: the same signature with every
// argument default omitted (the whole trailing default group — a partial
// omission would be invalid DDL) and an unchecked placeholder body
// (check_function_bodies=false stores string bodies verbatim, and
// CREATE OR REPLACE may change language/body/defaults, so the original SQL
// — already CREATE OR REPLACE — restores the real object as the "-final"
// stage). StubDeps are the edges the stub RETAINS (signature types); if
// those still form a cycle the plan preflight-fails.
Stub string
StubDeps []string
// OpaqueFrame marks SQL as raw bytes fetched under
// character_set_results=binary (MySQL object bodies): the writer wraps it
// in a TableX opaque frame — a frame marker plus a body-derived DELIMITER
// — and the splitter replays the framed bytes verbatim with no
// string/comment interpretation, so a non-UTF-8 or NO_BACKSLASH_ESCAPES
// body cannot be mangled or mis-split. Implies DELIMITER wrapping.
OpaqueFrame bool
// Pre/Post are complete session-guard statements the writer emits around
// SQL (outside any DELIMITER wrap): mysqldump-style save/set/restore of
// the object's creation context (sql_mode, character_set_client,
// collation_connection, event time_zone). Save variables MUST use
// object-local @saved_* names, never the preamble's @OLD_* names, which
// the postamble restores last.
Pre []string
Post []string
// Markers are pre-formatted inert TableX marker lines (FormatCollationMarker)
// emitted verbatim before the script — disclosure comments to external
// clients, warning events to TableX's own importer.
Markers []string
}
DumpScript is one self-contained piece of dump DDL. SQL holds one complete statement (or, for multi-statement bodies, a script) WITHOUT a trailing terminator — the exporter appends ';' or wraps in DELIMITER directives.
type DumpTableNode ¶
type DumpTableNode struct {
Name string // the table's node id ("relation:<schema>\x00<name>")
Deps []string
DeferrableDefaults map[string][]string // column name → node ids its DEFAULT expression references
DeferrableConstraints map[string][]string // constraint name → node ids its expression references
}
DumpTableNode is one table's dependency-graph metadata (see DumpPlan.TableNodes): hard edges plus the deferrable per-column DEFAULT and per-constraint expression edges the planner may CUT to break a cycle — cutting re-renders the CREATE without the clause via StagedTableDumper and re-adds it post-data (data INSERTs name every column explicitly, so a post-data SET DEFAULT never changes restored rows, and a re-added CHECK validates them).
type Dumper ¶
type Dumper interface {
// DumpTableCreate returns the restore-oriented CREATE statement(s) for one
// table, ending without a trailing terminator on the last statement.
DumpTableCreate(ctx context.Context, db *sql.DB, t TableRef) (string, error)
// DumpDataTables returns the subset of tables whose rows the data phase
// reads directly, preserving order. PostgreSQL excludes partition children:
// their structure rides with the parent and a SELECT on the parent already
// returns their rows (dumping both would duplicate every row).
DumpDataTables(ctx context.Context, db *sql.DB, scope Scope, tables []string) ([]string, error)
// DumpObjects returns everything beyond table DDL and data for the given
// tables. dbScope and structure are independent: dbScope=false (table-scope
// export) returns only objects belonging to the listed tables (triggers,
// indexes, constraints, owned sequences), while dbScope=true also covers
// database-scope objects (routines, views, events, standalone sequences).
// structure=false (data-only export) suppresses ALL structure-only DDL
// (routines/views/triggers/indexes/constraints/sequence ownership) so a
// data-only dump neither fails on nor wastes introspection it would
// discard, while still collecting the data-section items (sequence setval,
// matview refresh) pg_dump emits in a --data-only run.
DumpObjects(ctx context.Context, db *sql.DB, scope Scope, tables []string, dbScope, structure bool) (DumpPlan, error)
// ValueLiteral renders a scanned cell as an engine-correct SQL literal for
// the dump's INSERT statements: the binary form (X'…' vs '\x…'), MySQL's
// zero-date sentinel, and non-finite float handling all live here rather
// than in engine switches at the call site.
ValueLiteral(col ResultColumn, v Value) string
// DumpPreamble writes the session state a restore needs pinned before the
// script body (FK checks, sql_mode, string-escape settings, time zone);
// DumpPostamble restores it for sessions that outlive the script. Either
// may write nothing.
DumpPreamble(w io.Writer)
DumpPostamble(w io.Writer)
}
Dumper is an optional Dialect capability producing restore-equivalent dump DDL (discovered by type assertion, mirroring SchemaEditor/Monitor). The plain CreateSQL is display-oriented; these methods are restore-oriented: PostgreSQL strips foreign keys from the CREATE (returning them as post-data ALTERs so cyclic/self-referencing schemas restore), preserves PARTITION BY, and emits partition children with the parent; MySQL wraps procedural bodies for DELIMITER handling; SQLite returns the extra sqlite_master rows (indexes, triggers) the table's own row does not carry.
Fallback contract (asymmetric, by design): when a Dialect does NOT implement Dumper, the Connection passthroughs (DumpTableCreate/DumpDataTables/DumpObjects) degrade differently. DumpTableCreate hard-fails with ErrUnsupported — a table's restore DDL cannot be faked from the display-oriented CreateSQL without silently dropping restore-critical detail — while DumpDataTables returns the tables unchanged and DumpObjects returns an empty plan, so a minimal future dialect that implements no Dumper still exports its rows (data phase) and simply omits the extra objects. Implementers keep this split: the data/object passes degrade gracefully; the structure-DDL pass fails loudly.
type EventManager ¶
type ExecResult ¶
type ExecResult struct {
RowsAffected int64
}
ExecResult is the engine-neutral outcome of a non-query statement.
type ExportConnAdjuster ¶
type ExportConnAdjuster interface {
ExportConnParams(p ConnParams) ConnParams
}
ExportConnAdjuster is an optional Dialect capability: dialect-owned session pinning for a dedicated EXPORT connection's params (MySQL pins time_zone, sql_mode and sql_quote_show_create so dumps render restore-parsable). Contract: p is a value, and implementations MUST clone Params before modifying — the map is shared with ServerConfig.Params and the session's base params — and set the pins AFTER copying, so they overwrite any same-name key coming from operator config.
type FilePathValidator ¶
FilePathValidator is an optional Dialect capability for a file-backed engine (Capabilities.IsNetworkEngine false): reject an operator-configured database file path that the engine could technically open but that cannot work as a predefined server, at config load rather than at first use. SQLite refuses the in-memory spellings — every pooled or transient connection would open its own private empty database.
The error is surfaced to the operator verbatim (prefixed with the server's position and name), so it must read as a complete explanation on its own.
type ForeignKeyEditor ¶
type ForeignKeyEditor interface {
AddForeignKeySQL(t TableRef, name string, cols []string, refTable string, refCols []string, onUpdate, onDelete string) ([]string, error)
DropForeignKeySQL(t TableRef, name string) ([]string, error)
}
ForeignKeyEditor is the SchemaEditor half that adds and drops foreign-key constraints, split out for the same reason as ColumnModifier: SQLite cannot ALTER a table to add or drop one, so it carried two more error-only stubs. An engine that sets Capabilities.SupportsForeignKeyDDL MUST implement this.
The referential actions reaching these builders are pre-validated keywords, emitted verbatim.
type ForeignTableDumper ¶
type ForeignTableDumper interface {
IsForeignTable(ctx context.Context, db *sql.DB, scope Scope, name string) (bool, error)
DumpForeignTable(ctx context.Context, db *sql.DB, scope Scope, name string) (DumpPlan, error)
}
ForeignTableDumper (C4.3) is an optional Dialect capability for the SQL-export-only foreign-table path: IsForeignTable resolves a name the ordinary relation listings deliberately exclude (foreign tables never enter ListTables/ListTableNames — no browsing, CSV/JSON or data pass), and DumpForeignTable builds its STRUCTURE-ONLY plan (create + triggers + rules + comments + restore-prerequisite warnings; never any row read).
type GlobalDumper ¶
type GlobalDumper interface {
DumpGlobalObjects(ctx context.Context, db *sql.DB, structure bool) (DumpPlan, error)
}
GlobalDumper is an optional Dialect capability collecting DATABASE-GLOBAL (non-schema-owned) objects once per database, before the per-schema DumpObjects passes — the C3 architectural hook the C4 object breadth (casts, foreign-data wrappers/servers/user mappings) plugs its producers into. Globals live per database, so a server-scope export runs it once per database section.
type GrantSpec ¶
type GrantSpec struct {
Privileges []string
Database string
Schema string // PostgreSQL table scope; ignored elsewhere
Table string // "" → database scope
Grantee string
Host string // MySQL grantee host part
WithGrant bool
// Columns restricts the grant to specific columns of Table
// (GRANT SELECT (a, b) ON t). Empty — the default — is an object-wide
// grant. Names must have been matched against live introspection by the
// caller; the builders only quote them.
//
// Dropping this list does not fail safe, it WIDENS: "SELECT on two columns"
// silently becomes "SELECT on every column". So a dialect that cannot
// express column scope must not receive it — Connection.Grant/Revoke refuse
// a non-empty Columns unless the dialect is a ColumnPrivileger, and the
// builders re-check.
Columns []string
// DatabasePatterns carries the stored grant pattern(s) exactly as
// introspected (model.Privilege.StoredObject) for a MySQL database-scope
// REVOKE: pre-escaped as stored, used verbatim, one REVOKE per pattern.
// Empty (the default, and always for GRANT and other engines) falls back
// to escaping Database.
DatabasePatterns []string
}
GrantSpec describes one GRANT or REVOKE. Privileges must be pre-validated by the caller (grant: membership in GrantablePrivileges; revoke: membership in the freshly introspected grants) — the builders re-check defensively but the handler is the authority. Grantee "PUBLIC" is PostgreSQL's pseudo-role and is emitted as the bare keyword, never as a quoted identifier.
type Grouper ¶
type Grouper[K comparable, T any] struct { // contains filtered or unexported fields }
Grouper accumulates pointers to grouped elements keyed by K, preserving the order keys are first seen, then flattens them to a value slice. It removes the repeated "order []K + byKey map + lookup-or-create + rebuild tail" idiom the dialect Indexes/ForeignKeys introspection used to spell out at every call site (a row stream arrives grouped by name/id and must collapse to one element per group, preserving query order).
func NewGrouper ¶
func NewGrouper[K comparable, T any]() *Grouper[K, T]
NewGrouper returns an empty Grouper.
func (*Grouper[K, T]) GetOrAdd ¶
func (g *Grouper[K, T]) GetOrAdd(key K, create func() T) *T
GetOrAdd returns the existing element for key, or inserts create()'s result and returns a pointer to it. The returned pointer is stable across calls; callers mutate the group through it (e.g. appending columns to an index).
type IndexColumn ¶
type IndexColumn struct {
Name string
// Desc orders this key part descending. Only set it when
// IndexOptions().SupportsDesc — MariaDB, for one, PARSES the keyword and
// then ignores it, which is worse than refusing it.
Desc bool
// Prefix indexes only the leading Prefix characters/bytes of the column
// (MySQL's col(10)). Zero indexes the whole value. Only set it when
// IndexOptions().SupportsPrefix.
Prefix int
}
IndexColumn is one key part of an index, in key order.
type IndexOptioner ¶
type IndexOptioner interface {
IndexOptions() IndexOptions
}
IndexOptioner is the optional capability declaring the above. A dialect that implements it must honour what it claims: drivertest checks that each claimed option actually changes the statement AddIndexSQL builds, because a silently-dropped option is a promise the UI keeps making and the database never hears.
type IndexOptions ¶
type IndexOptions struct {
// Methods are the offerable access methods, engine spelling. Empty means
// the engine has no choice to make (MySQL: the storage engine decides).
Methods []string
// SupportsDesc: a descending key part is honoured, not merely parsed.
SupportsDesc bool
// SupportsPrefix: a key part may index a leading prefix of the value.
SupportsPrefix bool
// SupportsPartial: the index may carry a WHERE predicate.
SupportsPartial bool
}
IndexOptions reports which optional parts of an index an engine can express, so the UI offers exactly those and the handler refuses the rest. An engine with none of them omits IndexOptioner entirely and gets the zero value.
type IndexSpec ¶
type IndexSpec struct {
Name string
Columns []IndexColumn
Unique bool
// Method is the access method / index type: an entry from
// IndexOptions().Methods, or "" for the engine default. It is emitted as a
// keyword, so only an allowlisted value may reach it.
Method string
// Where is a partial index's predicate, already guarded by the caller. It
// is the one part of an index that is user-written SQL rather than an
// identifier or a keyword — see docs/security.md.
Where string
}
IndexSpec describes an index to create. It replaced a (name, cols, unique) argument list because everything past "unique" is engine-optional, and three more positional parameters would have forced every dialect to carry arguments it ignores.
The handler validates every part against IndexOptions() before this is built, so a dialect can assume it was handed only what it said it supports.
type Inheritor ¶
type Inheritor interface {
InheritanceParents(ctx context.Context, db *sql.DB, scope Scope, tables []string) (map[string][]string, error)
DumpInheritsChildCreate(ctx context.Context, db *sql.DB, t TableRef, parents []string) (string, error)
}
Inheritor is an optional Dialect capability for ordinary (INHERITS, non-partition) table inheritance. PostgreSQL implements it; MySQL/SQLite have no equivalent. InheritanceParents reports, per same-schema ordinary inheritance child among the given tables, its parent bare-names (ordered by inhseqno) — used to order parents before children and to decide the linked create. DumpInheritsChildCreate emits a child's CREATE TABLE with an INHERITS clause and local-only columns/constraints so the link and provenance survive.
type LexerProfile ¶
type LexerProfile struct {
// BackslashStrings: a backslash escapes the next character inside '…' and
// "…" string literals (MySQL default mode; never backtick identifiers).
BackslashStrings bool
// EscapeStringE: E'…' literals honor backslash escapes even though plain
// '…' strings do not (PostgreSQL).
EscapeStringE bool
// DollarQuotes: $tag$…$tag$ dollar-quoted literals (PostgreSQL). Engines
// where '$' is an identifier character must leave this off, or a$$b would
// open a quote.
DollarQuotes bool
// HashComments: '#' starts a line comment (MySQL).
HashComments bool
// DashCommentNeedsSpace: a `--` line comment requires following whitespace
// or end-of-line (MySQL); otherwise any `--` starts a comment.
DashCommentNeedsSpace bool
// NestedBlockComments: /* … */ block comments nest (PostgreSQL).
NestedBlockComments bool
// DelimiterDirectives: mysql-client DELIMITER directives are recognized,
// including the TableX opaque frames dumped routine bodies travel in.
DelimiterDirectives bool
// DollarInWords: '$' is an ordinary identifier character (MySQL/SQLite).
DollarInWords bool
// BracketIdentifiers: [name] is a quoted identifier, with ]] escaping a
// literal ']' — the T-SQL / Microsoft Access convention, which SQLite also
// accepts. Engines where '[' is an operator (PostgreSQL array subscripts)
// must leave this off. Without it a separator inside a bracket-quoted name
// (`[a;b]`) splits the statement.
BracketIdentifiers bool
// BatchSeparator, when non-empty, is a CLIENT-SIDE batch terminator
// recognized as the only content on its own line, case-insensitively —
// T-SQL's `GO`. It ends the current statement the way the statement
// separator does, but the word itself is never sent to the server. Empty
// (every engine here today) means the engine has none.
BatchSeparator string
// RoutineBodyRe, when non-nil, matches routine-creating statements whose
// BEGIN…END bodies hold internal semicolons, so the splitter tracks block
// depth instead of splitting inside the body. Engines whose bodies are
// quoted strings (PostgreSQL dollar quoting) leave it nil.
RoutineBodyRe *regexp.Regexp
// Returning describes which DML statement kinds support a RETURNING clause
// on this engine+version, so the SQL console can classify a RETURNING-bearing
// INSERT/UPDATE/DELETE/REPLACE/MERGE as row-returning (grid) rather than a
// bare Exec. Populated per dialect from flavor/version.
Returning ReturningCaps
}
LexerProfile describes the statement-lexing grammar of an engine's SQL scripts. The handler-side statement splitter consumes it instead of branching on raw engine names, so an engine's script grammar lives with its dialect: string-escape rules, comment forms, dollar quoting, mysql-client DELIMITER directives and procedural BEGIN…END body tracking.
func DefaultLexerProfile ¶
func DefaultLexerProfile() LexerProfile
DefaultLexerProfile is the grammar assumed for a dialect without a StatementLexer: standard-conforming quoting with PostgreSQL's extensions (dollar quotes, E'…' strings, nested block comments) — the same fallback the splitter's engine-name era used for unknown engines.
func ProfileOf ¶
func ProfileOf(d Dialect) LexerProfile
ProfileOf returns d's LexerProfile, or DefaultLexerProfile when the dialect does not implement StatementLexer.
type LoginDatabaseHint ¶
type LoginDatabaseHint struct {
Label string // field label ("" = the generic "Database")
Placeholder string
// Default is the value the form may pre-fill into an EMPTY field (and
// clear again when switching engines, never touching a user-typed value).
// The server-side authority for the same default is NormalizeParams.
Default string
// Note is a plain-text help sentence under the field ("" = none).
Note string
}
LoginDatabaseHint is per-engine presentation metadata for the ad-hoc login form's database field, carried as data-attributes on the engine <option> so the client script reads the dataset instead of naming engines.
type LoginFormHinter ¶
type LoginFormHinter interface {
LoginDatabaseHint() LoginDatabaseHint
}
LoginFormHinter is an optional Dialect capability supplying LoginDatabaseHint.
type MaintenanceDatabaseLister ¶
type MaintenanceDatabaseLister interface {
MaintenanceDatabases() []string
}
MaintenanceDatabaseLister is an optional Dialect capability naming the databases a session may connect to in order to run a statement it cannot run on its own connection — today, DROP DATABASE against the database the session is bound to (Capabilities.CanDropConnectedDatabase false). PostgreSQL returns "postgres" then "template1": a managed host often blocks the postgres database, which is exactly why user-database logins exist, so template1 is the fallback.
The slice is in preference order; the caller tries each in turn and skips the database being operated on. Engines that can act on their own connection (MySQL) or have no server-level databases at all (SQLite) omit it, and the operation is refused with a clear message rather than guessing a name.
Implementations MUST return a fresh slice — the value is shared by every session.
type Monitor ¶
type Monitor interface {
Status(ctx context.Context, db *sql.DB) ([]model.Variable, error)
Variables(ctx context.Context, db *sql.DB) ([]model.Variable, error)
Processes(ctx context.Context, db *sql.DB) (*ResultSet, error)
}
Monitor is an optional capability for server-level status, configuration variables and the process list. Handlers type-assert a Dialect to Monitor and gracefully degrade when an engine does not implement it (e.g. SQLite returns empty process lists). All built-in dialects implement it.
type NameLister ¶
type NameLister interface {
ListDatabaseNames(ctx context.Context, db *sql.DB) ([]model.Database, error)
ListTableNames(ctx context.Context, db *sql.DB, scope Scope) ([]model.Table, error)
}
NameLister is an optional Dialect capability providing cheap, identity-only listings for navigation trees, existence checks and counts. Implementations populate only Name/Schema/Type (tables) and Name/IsSystem (databases) and must avoid per-object statistics — the full ListDatabases/ListTables queries aggregate sizes and row counts, which on MySQL and PostgreSQL touch table statistics or stat the relation files for every object. Engines whose full listings are already statistics-free (SQLite) omit it and the Connection passthrough falls back.
type NestedGrouper ¶
type NestedGrouper[K1, K2 comparable, T any] struct { // contains filtered or unexported fields }
NestedGrouper is Grouper's two-level form: rows arrive keyed by an outer group (table) and an inner group (constraint name) and collapse to one element per inner group, preserving first-seen order at both levels. It absorbs the outer "map[K1]*Grouper + ordered key slice + rebuild loop" wrapper the MySQL and PostgreSQL bulk foreign-key introspections both spelled out around the shared inner Grouper.
func NewNestedGrouper ¶
func NewNestedGrouper[K1, K2 comparable, T any]() *NestedGrouper[K1, K2, T]
NewNestedGrouper returns an empty NestedGrouper.
func (*NestedGrouper[K1, K2, T]) GetOrAdd ¶
func (n *NestedGrouper[K1, K2, T]) GetOrAdd(outer K1, inner K2, create func() T) *T
GetOrAdd returns the element for (outer, inner), inserting create()'s result on first sight. Pointer semantics mirror Grouper.GetOrAdd: callers mutate the group through the stable returned pointer.
func (*NestedGrouper[K1, K2, T]) Map ¶
func (n *NestedGrouper[K1, K2, T]) Map() map[K1][]T
Map flattens to outer key → grouped values (inner first-seen order).
type Pagination ¶
Pagination is the engine-neutral limit/offset for browse pages. Offset is int64 so a 32-bit build can still address rows beyond 2^31.
type ParamsNormalizer ¶
type ParamsNormalizer interface {
NormalizeParams(p ConnParams) ConnParams
}
ParamsNormalizer is an optional Dialect capability applied by the login handler to freshly-built connection params, so engine-specific defaults live with the dialect instead of the boundary code. PostgreSQL defaults an empty Database to "postgres". This is deliberately a params rewrite, never a BuildDSN-internal default: params.Database must stay observably set — it flows into the session's base params and the server-connection reuse test. Implementations treat p as a value; they may set scalar fields but must clone Params before modifying it (the map is shared with the server config).
type Pinned ¶
type Pinned struct {
// contains filtered or unexported fields
}
Pinned is a dedicated single physical database connection for running multi-statement scripts (SQL imports, the console). Pool execution would let successive statements land on different physical connections, so session-scoped state (SET sql_mode / time_zone, FOREIGN_KEY_CHECKS=0, PRAGMA foreign_keys=OFF) would neither reliably apply to later statements nor be cleaned up when a script stops at the first error. A Pinned connection guarantees both: every statement runs on the one connection, and Close discards it (together with its private pool) instead of returning dirty session state to a shared pool.
func OpenPinned ¶
OpenPinned dials a dedicated connection using the dialect and params. A MaxOpenConns(1) pool alone would not be enough — a pool may still recycle the physical session mid-script — so the single connection is checked out up front and held until Close.
Like Open, it loads ServerInfo and routes the dialect through Specialize: Pinned.Dialect feeds driver.ProfileOf, which gates the console/import splitter's RETURNING classification on the detected flavor and version. An unspecialized dialect reports the registered zero value, so MariaDB's `DELETE … RETURNING` would run as an Exec and its rows be discarded. Reading the facts from THIS session (rather than inheriting the login pool's) is also the more correct answer for the session-scoped ones — MySQL's sql_mode.
func (*Pinned) Close ¶
Close releases the pinned connection and its private pool, discarding the physical connection along with any session state the script left behind.
func (*Pinned) ExplainSQL ¶
ExplainSQL returns the exact statement Explain would execute, so the console can label the result with what really ran.
type PoolOpener ¶
type PoolOpener interface {
OpenPool(p ConnParams) (*sql.DB, error)
}
PoolOpener is an optional Dialect capability: a network engine implements it to build its *sql.DB pool through a driver.Connector so ConnParams.DialControl can be applied to every connection (the dial-time SSRF guard). Engines without a network (SQLite) omit it, and Open falls back to the generic sql.Open(SQLDriverName(), BuildDSN()) path.
type PrivilegeManager ¶
type PrivilegeManager interface {
GrantablePrivileges(table bool) []string // curated allowlist: table or database scope
GrantSQL(g GrantSpec) ([]string, error)
RevokeSQL(g GrantSpec) ([]string, error)
}
PrivilegeManager is an optional, write-only Dialect capability for GRANT/REVOKE (the read side stays on Privileger). GrantablePrivileges is the single source of truth for both the grant form's checkbox set and grant validation. RevokeSQL deliberately accepts keywords outside that set — an already-present grant of a version-specific privilege (e.g. PostgreSQL 17's MAINTAIN) must stay revokable — but every keyword still passes ValidPrivilegeKeyword before it is emitted.
type Privileger ¶
type Privileger interface {
Privileges(ctx context.Context, db *sql.DB, ref TableRef) ([]model.Privilege, error)
}
Privileger is an optional Dialect capability for listing privilege grants scoped to a database (ref.Table == "") or a specific table. Engines without an access-control system (SQLite) do not implement it, and the handlers degrade gracefully.
type ProcessManager ¶
type ProcessManager interface {
// ProcessIDColumn is the Processes() column whose value KillProcessSQL
// takes ("Id" on MySQL, "pid" on PostgreSQL).
ProcessIDColumn() string
// KillProcessSQL terminates the session with this identifier.
//
// The id is an int64, not a string, and that is a deliberate part of the
// contract rather than a convenience: neither engine accepts a placeholder
// where the identifier goes (MySQL's KILL takes no parameters at all), so
// the value is formatted into the statement — and an integer parsed by the
// caller cannot carry anything else. Do not widen this to a string.
KillProcessSQL(id int64) string
}
ProcessManager is an optional capability: terminate a server session listed by Monitor.Processes. Engines with no session table (SQLite) omit it and the process page stays read-only.
The process list is an opaque *ResultSet — each engine's own columns — so the dialect also names the column holding the identifier. That pairing is the whole interface: without it a caller would have to guess which column of SHOW FULL PROCESSLIST or pg_stat_activity is the one to kill.
type ProgramKind ¶
type ProgramKind string
ProgramKind names a STORED PROGRAM — a routine, trigger or event — as opposed to the relation kinds (ObjectTable / ObjectView / ObjectMatView) that SchemaEditor's DropObjectSQL and RenameObjectSQL take. The two vocabularies are deliberately separate: they name different things, are consumed by different capabilities, and a single Object* family spanning both would invite passing a trigger where a table is meant. "Stored program" is MySQL's own term for the group.
It is a closed set the caller maps a listing row onto, so a dialect only ever sees a kind for an object it listed in the first place.
const ( ProgramProcedure ProgramKind = "procedure" ProgramFunction ProgramKind = "function" ProgramTrigger ProgramKind = "trigger" ProgramEvent ProgramKind = "event" )
func ProgramDDLKind ¶
func ProgramDDLKind(stmt string) (ProgramKind, bool)
ProgramDDLKind reports the stored-program kind a CREATE statement declares. The editor uses it to refuse a statement that is not a CREATE at all, or creates a different kind of object than the page it was submitted from — so "edit this trigger" cannot quietly run something else.
type ResultColumn ¶
type ResultColumn struct {
Name string
DBType string // DatabaseTypeName(), e.g. "VARCHAR", "INT4", "BLOB"
Numeric bool
Binary bool
}
ResultColumn is the metadata for one result column.
type ResultSet ¶
type ResultSet struct {
Columns []ResultColumn
Rows [][]Value
Truncated bool // true when more rows existed than the cap returned
}
ResultSet is an engine-neutral table of results, produced by scanning any *sql.Rows generically. It backs both the browse grid and the SQL console, so every engine's output renders through the same template path.
func ScanResult ¶
ScanResult reads up to limit rows from rows into a ResultSet, eliding text cells beyond MaxCellBytes. A limit <= 0 uses the default safety cap. The caller retains ownership of rows and must not have consumed them yet; ScanResult closes them.
func ScanResultVerbatim ¶
ScanResultVerbatim is ScanResult with no per-cell size cap, for the one caller that must round-trip a cell rather than display it: the row-edit form, whose prefilled inputs are posted back as the row's new values. Everything else must use ScanResult.
type ReturningCaps ¶
type ReturningCaps struct {
Insert, Update, Delete, Replace, Merge bool
}
ReturningCaps records, per leading statement keyword, whether a RETURNING clause is supported. A false gate means a `… RETURNING …` on that statement is run as a plain Exec (the statement still executes; only the grid-vs-count presentation differs), which is correct on engines/versions lacking RETURNING there and avoids misrouting an unquoted identifier named `returning`.
func (ReturningCaps) Allows ¶
func (rc ReturningCaps) Allows(keyword string) bool
Allows reports whether an uppercased leading keyword supports RETURNING.
type RoleGrant ¶
type RoleGrant struct {
Role string
RoleHost string // MySQL 8 role account host; ignored by MariaDB and PostgreSQL
Member string
MemberHost string // MySQL/MariaDB grantee host
// AdminOption lets the member grant the role onward. Ignored by REVOKE,
// which removes the membership entirely.
AdminOption bool
}
RoleGrant describes one GRANT/REVOKE of a role to an account. Both ends must have been matched against the live account listing by the caller; the builders only quote them.
type RoleManager ¶
type RoleManager interface {
RoleMemberships(ctx context.Context, db *sql.DB) ([]model.RoleMembership, error)
GrantRoleSQL(g RoleGrant) ([]string, error)
RevokeRoleSQL(g RoleGrant) ([]string, error)
}
RoleManager is an optional Dialect capability for role membership — the grants that hand one account another's privileges wholesale. Read and write live on one interface because, as with column grants, a membership that cannot be listed cannot be revoked.
It is paired with Capabilities.SupportsRoles, which is VERSION-GATED on MySQL/MariaDB (roles arrived in MySQL 8.0 and MariaDB 10.0.5) and must fail CLOSED on an unknown version: the catalog table simply does not exist on an older server, so guessing "supported" turns the page into an error instead of a hidden section. That is the opposite of SupportsColumnRename, which fails open because only one old flavor lacks the statement.
The two engine families disagree about what a role IS, and the interface deliberately does not paper over it: a MySQL 8 role is an ordinary account ('r'@'%'), a MariaDB role is a distinct kind of row with no host, and every PostgreSQL role can be granted to any other. Each dialect addresses its own catalog; handlers only move model.RoleMembership around.
type RoutineGrant ¶
type RoutineGrant struct {
Scope Scope
Routine model.Routine
Privileges []string
Grantee string
Host string // MySQL grantee host part
WithGrant bool
}
RoutineGrant describes one GRANT/REVOKE on a stored routine. Routine is the object as INTROSPECTION returned it, not as a request named it: PostgreSQL needs its ArgSignature to address one of several overloads, and both engines need its Type to pick the FUNCTION or PROCEDURE keyword.
type RoutineManager ¶
type RoutineManager interface {
DropRoutineSQL(s Scope, r model.Routine) ([]string, error)
NewRoutineTemplate(kind ProgramKind) string
}
RoutineManager administers stored routines, as TriggerManager and EventManager do for triggers and events (see ProgramKind). They are three interfaces rather than one because engines support the three kinds independently — SQLite has triggers but neither routines nor events, and only MySQL has events — and folding them together would hand SQLite two methods that never do anything but return an error. That is precisely the shape ColumnModifier and ForeignKeyEditor were split out of SchemaEditor to avoid.
Each takes the LISTED model object rather than a bare name. The caller resolves it from introspection first, so the identifier reaching QuoteIdent is one the catalog returned; the engine then reads whatever else it needs from the same object — PostgreSQL needs a trigger's table to say DROP TRIGGER … ON …, and a routine's argument signature to name an overloaded function unambiguously. The New*Template methods return the skeleton the "new object" editor opens with. They are engine SQL — MySQL's BEGIN…END body and PostgreSQL's dollar-quoted plpgsql block share no syntax — so the dialect owns them; the editor never assembles a statement of its own.
type RoutinePrivileger ¶
type RoutinePrivileger interface {
// RoutineGrantablePrivileges is the curated allowlist for routine scope —
// EXECUTE everywhere, plus MySQL's ALTER ROUTINE.
RoutineGrantablePrivileges() []string
RoutinePrivileges(ctx context.Context, db *sql.DB, s Scope, r model.Routine) ([]model.Privilege, error)
GrantRoutineSQL(g RoutineGrant) ([]string, error)
RevokeRoutineSQL(g RoutineGrant) ([]string, error)
}
RoutinePrivileger is an optional capability for routine-scope grants (GRANT EXECUTE ON FUNCTION …). Read and write on one interface, for the reason the other two privilege capabilities share: an unlistable grant is an unrevokable one.
A routine is not a relation, so this deliberately does NOT reuse Privileger's TableRef: PostgreSQL addresses a routine by schema, name AND identity arguments, which no TableRef has a place for, and silently dropping them would target whichever overload the catalog happened to return first.
type RowEstimator ¶
type RowEstimator interface {
EstimateRows(ctx context.Context, db *sql.DB, t TableRef) (int64, error)
}
RowEstimator is an optional Dialect capability returning the engine's statistics-based row-count estimate for a table (MySQL information_schema TABLE_ROWS, PostgreSQL reltuples, SQLite sqlite_stat1). It returns -1 when no usable estimate exists (never-analyzed relation, view — SQLite returns -1 until ANALYZE has run); callers then fall back to an exact COUNT(*).
type SchemaEditor ¶
type SchemaEditor interface {
ColumnTypes() []string // curated base-type allowlist for the UI + validation
// CreateTableSQL builds the statements for a NEW table: t names it (the
// handler validates the name and non-existence first), cols is the full
// ordered column list, pk the PRIMARY KEY column names (each must appear in
// cols; empty for no PK). One CREATE TABLE statement — PK inline or as a
// table constraint — plus any trailing comment statements the engine needs.
// Implementations reject empty cols and unknown pk entries via
// ValidateCreateTable.
CreateTableSQL(t TableRef, cols []ColumnSpec, pk []string) ([]string, error)
AddColumnSQL(t TableRef, c ColumnSpec) ([]string, error)
DropColumnSQL(t TableRef, col string) ([]string, error)
// DropObjectSQL emits the DROP for a table, view or materialized view. kind
// is one of the Object* constants; matview is PostgreSQL-only (other engines
// treat it as a view). Routing DROP through the dialect keeps a
// DROP TABLE-on-a-view (which errors on every engine) from ever being emitted.
DropObjectSQL(t TableRef, kind string) ([]string, error)
// RenameObjectSQL renames a table, view or materialized view to newName. kind
// selects the correct statement where engines differ: a MySQL view cannot be
// renamed with ALTER TABLE (error 1347) and needs RENAME TABLE; a PostgreSQL
// materialized view uses ALTER MATERIALIZED VIEW.
RenameObjectSQL(t TableRef, newName, kind string) ([]string, error)
AddIndexSQL(t TableRef, spec IndexSpec) ([]string, error)
DropIndexSQL(t TableRef, name string) ([]string, error)
}
type SchemaManager ¶
type SchemaManager interface {
CreateSchemaSQL(name string) string
// DropSchemaSQL drops the schema and every object in it (CASCADE); the
// handler shows an explicit confirmation before invoking it.
DropSchemaSQL(name string) string
}
SchemaManager is an optional Dialect capability for schema-level management inside one database (PostgreSQL). Engines whose Capabilities report HasSchemas implement it. Names are quoted with QuoteIdent in the builders; the handler validates them first (ValidNewIdentifier for create, introspection match for drop). The statements must run on a connection bound to the target database — schemas are per-database objects.
type Scope ¶
Scope identifies what to introspect: a database, optionally narrowed to a schema. For engines without schemas, Schema is empty.
type SearchCaster ¶
SearchCaster is an optional Dialect capability returning the SQL expression used to LIKE-match a column in the search features. PostgreSQL implements it to cast the quoted identifier to text — uuid/json/bool/enum/inet/array columns reject a bare LIKE outright there, which silently dropped whole tables from database search and failed table search. Engines whose LIKE coerces implicitly (MySQL, SQLite) omit it and the identifier is used as-is.
type ServerDumpFramer ¶
type ServerDumpFramer interface {
ServerDumpProfile() ServerDumpProfile
// WriteServerDumpHeader writes the engine's help comment lines for the
// dump header (may write nothing).
WriteServerDumpHeader(w io.Writer)
// WriteDatabaseSectionHeader introduces one database's section: the
// executable CREATE DATABASE …/USE pair (MySQL, preserving the
// introspected default collation), the \connect meta-command (PostgreSQL),
// or nothing (SQLite). collation is the database's introspected default
// collation ("" when unknown/unsupported).
WriteDatabaseSectionHeader(w io.Writer, name, collation string)
// UnaddressableDatabase reports a human-readable reason the named database
// cannot be addressed by this dump format ("" = addressable). PostgreSQL
// skips names containing \r or \n: a psql \connect argument cannot
// continue past end-of-line, and the residual fragment would leak into the
// next section as executable SQL (the pg_dumpall CVE-2016-5424 class).
UnaddressableDatabase(name string) string
}
ServerDumpFramer is an optional Dialect capability owning everything engine-specific about a server-scope SQL dump's framing: which databases a single script can address, how a database section is introduced, whether the preamble is global or per-section, and the dump-header help text. The handler loops over databases and delegates; it never branches on an engine name.
type ServerDumpProfile ¶
type ServerDumpProfile struct {
// PerSectionPreamble: the session preamble/postamble must be re-emitted
// inside each database section (PostgreSQL: session SETs do not survive
// \connect) instead of once globally.
PerSectionPreamble bool
// UsesConnectMarkers: sections switch databases via psql-style \connect
// meta-commands, which the server-scope import honors (the import-side
// mirror of this flag — export and import share it).
UsesConnectMarkers bool
// FormNote is the plain-text help sentence the export form shows for a
// server-scope dump ("" = none).
FormNote string
}
ServerDumpProfile describes the framing shape of an engine's SERVER-scope SQL dump. These are framing-owned flags on purpose: capability bits like DatabasesShareConnection describe connection topology, and any truth-value match with framing needs is coincidence — a new dialect sets its framing here and stays correct.
type ServerInfo ¶
type ServerInfo struct {
Engine string // dialect Name(): "mysql", "postgres", "sqlite"
Flavor string // "MySQL", "MariaDB", "PostgreSQL", "SQLite"
Version string // full version string
User string // CURRENT_USER as reported by the server
Charset string // server/connection default charset
Collation string // server/connection default collation
Host string // connection target host (for display only)
Port int
Database string // current database, when bound to one
// SQLMode carries the MySQL/MariaDB @@SESSION.sql_mode at connect time, so a
// dialect can specialize string escaping for NO_BACKSLASH_ESCAPES via the
// optional WithServerInfo hook. Empty for engines that do not report it.
SQLMode string
}
ServerInfo is the engine-neutral summary shown on the home page and used to decide MariaDB-vs-MySQL display, schema handling, etc.
type ServerSpecializer ¶
type ServerSpecializer interface {
WithServerInfo(info ServerInfo) Dialect
}
ServerSpecializer is an optional Dialect capability: return a COPY of the dialect specialized for one connection's server facts (MySQL derives NO_BACKSLASH_ESCAPES from sql_mode plus the flavor/version that gate introspection columns and the RETURNING lexer profile; PostgreSQL records the major version that gates MERGE … RETURNING).
Contract: the receiver must be treated as a value and the specialized copy returned — never mutate the registered singleton, which every session shares. Every path that establishes a connection MUST route its dialect through Specialize; a path that skips it leaves the registered zero value in play, so every version/flavor gate silently fails closed (this is exactly how the SQL console once discarded MariaDB's `DELETE … RETURNING` rows).
type Sort ¶
type Sort struct {
Column string // must be validated against real columns before use
Descending bool
}
Sort describes an ORDER BY clause built from a validated column name.
type StagedTableDumper ¶
type StagedTableDumper interface {
DumpTableCreateStaged(ctx context.Context, db *sql.DB, t TableRef, parents []string, stripDefaults, stripConstraints []string) (string, []DumpScript, error)
}
StagedTableDumper is an optional Dialect capability the C3 cycle resolver uses: re-render one table's create with the named columns' DEFAULT clauses and the named inline constraints OMITTED, returning the reduced create plus the deferred post-data scripts (ALTER … SET DEFAULT / ADD CONSTRAINT, with any constraint comment moved along — a comment cannot ride a create that no longer declares its constraint). parents mirrors DumpInheritsChildCreate (nil for a standalone/plain table).
type StatementEvent ¶
type StatementEvent struct {
// SQL is the statement as it was sent. The caller bounds its length before
// recording it: an import statement can be megabytes.
SQL string
// Rows is the affected-row count for a mutation, or -1 where the engine or
// the code path does not report one.
Rows int64
Duration time.Duration
// Err is the statement's error, or nil. A statement error may echo the
// statement itself, which is already being recorded, so it carries nothing
// the trail does not already hold.
Err error
// UserSQL distinguishes SQL the USER wrote (the console, an import) from SQL
// TableX generated on their behalf (the Drop button, the structure editor).
// An auditor reading "DROP TABLE orders" wants to know which it was.
UserSQL bool
}
StatementEvent is one statement, as the observer sees it.
type StatementLexer ¶
type StatementLexer interface {
LexerProfile() LexerProfile
}
StatementLexer is an optional Dialect capability supplying the engine's script-lexing grammar. Dialects that omit it get DefaultLexerProfile.
type StatementObserver ¶
type StatementObserver func(ctx context.Context, ev StatementEvent)
StatementObserver is notified after a statement runs. It is the seam the audit trail hangs on, and it exists here — as a plain callback rather than an import of internal/audit — so this package keeps knowing nothing about the application above it.
WHICH statements are reported is deliberately asymmetric:
- On a shared pool (Connection), only MUTATIONS — Exec and ExecScript. The row-returning paths are how Browse, introspection and every count run, so reporting them would bury the trail in reads TableX generated for its own rendering.
- On a pinned connection (Pinned), EVERYTHING. A pinned connection exists only to run a script the user wrote themselves — the SQL console and SQL import — so a SELECT there is as much a user action as a DROP, and an auditor wants both.
It must not block: it runs on the request's goroutine, immediately after the statement it describes.
type StorageDDL ¶
type StorageDDL struct {
// ID types a short ASCII identifier — at most 64 characters — that must be
// usable as a PRIMARY KEY and inside an index. That requirement is why it is
// not simply Text: MySQL cannot index a TEXT column without a prefix length.
ID string
// Text types unbounded UTF-8 text.
Text string
// Int64 types a signed 64-bit integer.
Int64 string
// TableOptions is appended verbatim to every CREATE TABLE (empty for engines
// that need nothing). It must begin with a space if non-empty. MySQL pins
// both the character set — a server defaulting to latin1 would otherwise
// silently mangle non-ASCII text — and a transactional engine, which the
// storage layer's atomic updates require.
TableOptions string
}
StorageDDL names the column types internal/storage's schema needs, in one engine's spelling, plus whatever must be appended to a CREATE TABLE.
The vocabulary is three types long on purpose: every field internal/storage stores is an identifier, some text, or a 64-bit integer. Most notably there is no timestamp — an instant is an Int64 of Unix MICROSECONDS, UTC. A portable timestamp type does not exist across these engines (SQLite has no date type at all; MySQL and PostgreSQL disagree about whether a column carries a zone, and their drivers disagree about what Go type to scan one into), so the schema avoids the question rather than papering over it. Comparisons and ordering on an integer instant behave identically everywhere.
type StorageHost ¶
type StorageHost interface {
StorageDDL() StorageDDL
}
StorageHost is an optional Dialect capability for engines that can host TableX's OWN metadata database — the durable state described in internal/storage, as opposed to the databases a user administers. An engine that omits it simply cannot be named as the storage backend (config refuses it by name at startup); every other capability is unaffected.
It is deliberately the smallest interface in this file. The metadata schema is written once, by TableX, in portable SQL — CREATE TABLE IF NOT EXISTS, positional placeholders, no engine-specific clauses — so the only thing left for a dialect to supply is the SPELLING of a handful of column types.
type TableMaintainer ¶
type TableMaintainer interface {
TableMaintenanceOps() []TableMaintenanceOp
TableMaintenanceSQL(t TableRef, op string) (string, error)
}
TableMaintainer is an optional capability exposing the engine's table maintenance commands (MySQL's OPTIMIZE/CHECK/REPAIR/ANALYZE, PostgreSQL's VACUUM/ANALYZE/REINDEX, SQLite's ANALYZE).
The offered set is data, not a fixed enum, because the commands have almost nothing in common across engines; the handler renders whatever the dialect lists and will only run a name from that same list.
Results come back as a ResultSet: MySQL's CHECK and REPAIR report their findings as rows, and discarding them would make "check this table" useless. Engines whose commands return nothing simply produce no rows.
type TableMaintenanceOp ¶
type TableMaintenanceOp struct {
Name string // stable key the form posts back ("optimize")
Label string // button text ("Optimize")
Note string // one line explaining what it does, shown as the button's title
// Confirm, when non-empty, is the prompt shown before running. Set it for
// anything that takes a heavy lock or rewrites the table — these operations
// are not destructive, but running one on a large production table by
// accident is its own kind of damage.
Confirm string
}
TableMaintenanceOp is one maintenance command an engine offers for a table.
type TeardownAuditor ¶
type TeardownAuditor interface {
AuditTeardown(ctx context.Context, db *sql.DB, planned []TeardownDrop) ([]string, error)
}
TeardownAuditor is an optional Dialect capability (C6): a WARN-ONLY, non-destructive audit of a drop-first teardown, run once per database when the user asked to drop first. It probes the SOURCE catalog for objects that depend on a planned DROP but are NOT themselves dropped by this dump — an external view over a dumped table, an out-of-scope index using a dumped operator class, an out-of-scope inheritance/partition child of a dumped parent — and returns advisory warnings naming the blocked statement.
It is best-effort by construction: the audit runs on the source connection, so blockers that exist only in the RESTORE TARGET are unknowable. It never fails the export, never escalates a DROP to CASCADE, and never suppresses a drop; a fresh target no-ops every `DROP … IF EXISTS` regardless.
type TeardownDrop ¶
TeardownDrop is one planned teardown DROP handed to a TeardownAuditor: the object's graph node id (in the dialect's own id spelling — the auditor is the dialect that minted it) and the exact statement, so a warning can name the precise blocked drop.
type TriggerManager ¶
type Tuning ¶
type Tuning struct {
// MaxOpenConns / MaxIdleConns size a pool. OpenPinned ignores both: a pinned
// script is one physical session by definition.
MaxOpenConns int
MaxIdleConns int
// ReadStmtTimeout overrides the per-statement budget for generated reads
// (see the ReadStmtTimeout constant for exactly which statements).
ReadStmtTimeout time.Duration
}
Tuning is the operator-settable connection behaviour Open applies. Zero fields fall back to the package defaults, so a caller that does not care can leave the whole struct zero.
type UserManager ¶
type UserManager interface {
CreateUserSQL(u UserSpec) ([]string, error)
AlterUserSQL(u UserSpec) ([]string, error) // set password and/or role attributes
DropUserSQL(name, host string) ([]string, error)
}
UserManager is an optional, write-only Dialect capability for account administration (the read side stays on the mandatory ListUsers). Builders quote account parts internally — MySQL as string literals, PostgreSQL as identifiers — and return complete statements for Connection.ExecScript. Engines without an account system (SQLite) do not implement it and the UI hides the controls.
type UserSpec ¶
type UserSpec struct {
Name string
Host string // MySQL account host part ('user'@'host'); ignored by role engines
Password string
SetPassword bool // distinguishes "set this password (possibly blank)" from "leave unchanged"
CanLogin bool // PostgreSQL LOGIN attribute; MySQL accounts always log in
Super bool // PostgreSQL SUPERUSER
CreateDB bool // PostgreSQL CREATEDB
CreateRole bool // PostgreSQL CREATEROLE
}
UserSpec describes a database account to create or alter. Password is the plaintext the operator typed: builders emit it only through QuoteString and callers must never log, flash or render it (or any SQL embedding it).
type Value ¶
type Value struct {
Null bool
Binary bool
Str string
Bytes []byte // raw binary content; populated only by StreamResult
}
Value is one scanned cell. Null is rendered specially; Binary cells present a short placeholder in Str and carry the raw bytes (for hex export) only on the streaming path — buffered results drop them so a page of BLOBs cannot pin hundreds of megabytes.
type ValueListTyper ¶
type ValueListTyper interface {
// ValueListTypes returns the base types requiring a value list, spelled as
// they appear in ColumnTypes().
ValueListTypes() []string
// ValueListType assembles the type. base is an allowlisted ColumnTypes()
// entry; values is the user's list, non-empty, in order. It returns an
// error for a list this engine cannot accept (a duplicate, or more members
// than the type allows).
ValueListType(base string, values []string) (string, error)
}
ValueListTyper is the optional capability for column types defined by a LIST OF VALUES rather than a length or precision — MySQL's ENUM and SET. The column editor needs two things from the dialect to offer them: which of its ColumnTypes() entries take a value list, and how that list is spelled.
It is the dialect's job because the spelling is engine syntax. PostgreSQL has enumerated types but they are user-defined types (CREATE TYPE … AS ENUM), not an inline column type, and it has no SET at all; SQLite has neither. Both therefore omit this, and the editor's value-list control does not appear.
ValueListType is the ONLY place a user-supplied value reaches a type definition, so implementations MUST quote each value through QuoteString. The values arrive exactly as typed — they are data, not identifiers, and nothing upstream can allowlist them.
type ValueLiteralHooks ¶
type ValueLiteralHooks struct {
// BinaryLiteral spells a binary cell's raw bytes as an SQL literal.
BinaryLiteral func(b []byte) string
// TextSpecial, when non-nil, may short-circuit a non-null, non-binary
// value with an engine literal (ok=true) before any numeric handling.
TextSpecial func(col ResultColumn, s string) (lit string, ok bool)
// NonFinite maps a NonFiniteFloat class ("nan", "+inf", "-inf") to the
// engine literal. nil applies the fallback finite guard: a non-finite
// token is not a valid bare numeric literal, so it is emitted as quoted
// text.
NonFinite func(class string) string
}
ValueLiteralHooks parameterize RenderValueLiteral with the two things the engines genuinely disagree on:
- binary spelling: X'…' (MySQL/SQLite/fallback) vs '\x…' (PostgreSQL);
- non-finite floats: NULL (MySQL, SQLite NaN), quoted tokens ('NaN'/'Infinity', PostgreSQL), overflowing exponents (SQLite ±Inf), or the fallback's quote-the-text finite guard.
TextSpecial is a PRE-PASS hook that runs on the text path BEFORE the numeric check (MySQL's zero-date sentinel lives on a temporal column's text path), while NonFinite runs INSIDE the numeric branch — two distinct insertion points by design.
type VersionFloor ¶
type VersionFloor interface {
// ServerBelowFloor reports the documented floor for the connected flavor, and
// whether this server is older than it. An implementation that cannot parse
// the version must answer false: guessing "too old" would cry wolf on every
// unfamiliar build string.
ServerBelowFloor() (floor string, below bool)
}
VersionFloor is implemented by a dialect that knows the oldest server release its introspection and DDL rely on. TableX used to perform no version check at all, so connecting something older degraded in place — a missing catalog column surfaced as an empty listing or a confusing error, at the point of use, with nothing pointing at the real cause.
It reports rather than refuses. An operator with an older server may still get most of the tool working, and locking them out over a documented feature floor would be a worse trade than telling them plainly. The check is per CONNECTION because that is the only place the server version is known: a specialized dialect has already read it.
type ViewDumper ¶
type ViewDumper interface {
DumpView(ctx context.Context, db *sql.DB, scope Scope, name string, withData bool) (DumpPlan, error)
}
ViewDumper is an optional Dialect capability: dump a SINGLE view or materialized view's restore-oriented DDL. A table-scope SQL export whose target is a view needs this because the schema-wide view pass inside DumpObjects runs only for a whole-database dump (dbScope) — without it, the table path emits a physical CREATE TABLE snapshot (PostgreSQL), errors (SQLite), or mis-scans the object (MySQL). The returned plan populates Views with the CREATE [MATERIALIZED] VIEW (plus its object/column comments and, for SQLite, the view's INSTEAD OF triggers); for a populated materialized view with withData set it appends a REFRESH to PostData. The caller emits no CREATE TABLE and no row INSERTs for the object.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package drivertest is the reusable conformance suite for a TableX Dialect.
|
Package drivertest is the reusable conformance suite for a TableX Dialect. |
|
Package mysql implements the TableX Dialect for MySQL and MariaDB.
|
Package mysql implements the TableX Dialect for MySQL and MariaDB. |
|
Package postgres implements the TableX Dialect for PostgreSQL using the pure-Go jackc/pgx/v5 driver (via its database/sql adapter).
|
Package postgres implements the TableX Dialect for PostgreSQL using the pure-Go jackc/pgx/v5 driver (via its database/sql adapter). |
|
Package sqlite implements the TableX Dialect for SQLite using the pure-Go modernc.org/sqlite driver (no CGo).
|
Package sqlite implements the TableX Dialect for SQLite using the pure-Go modernc.org/sqlite driver (no CGo). |