dialect

package
v10.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 15, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package dialect names the SQL dialects the module's SQL-emitting packages support, and carries the small helpers every one of them otherwise reimplements: bind-marker rendering, identifier vetting, and DDL statement splitting.

It exists to be a leaf. database/migrate, outbox, and authorization/database all speak the same three dialects, and their migrations subpackages cannot import their parents without closing a cycle through the parents' tests — so before this package, each of the five declared its own Dialect type and tests converted between them. One shared type makes those conversions unrepresentable.

Index

Constants

View Source
const PostgresNotifyStatement = `SELECT pg_notify($1, '')`

PostgresNotifyStatement emits a payload-free notification on the channel bound to it, waking anything listening on that channel — see database/postgres/pgnotify for the other end.

The payload is empty on purpose. Postgres collapses duplicate (channel, payload) pairs within a transaction, so a transaction that notifies fifty times sends one notification, and there is nothing in it for a consumer to come to depend on. The channel is bound rather than interpolated; the listening side has to render it into a LISTEN, which takes no parameters, so it is vetted with ValidIdentifier there.

It lives here, in the leaf every SQL-emitting package already imports, rather than beside the listener: outbox serves three dialects and workqueue serves one, and neither should take a pgx dependency to reach a constant.

Variables

View Source
var (
	IdentifierChars     = charset.ASCIIAlphanumeric.Union(charset.Bytes('_'))
	IdentifierLeadChars = charset.ASCIILetters.Union(charset.Bytes('_'))
)

IdentifierChars and IdentifierLeadChars are the alphabet a SQL identifier is drawn from, and the narrower one its first character comes from. A leading digit is excluded so a bare name is never mistakable for a number.

They are exported so that the rules built on top of an identifier — a table prefix, which is an identifier fragment or nothing — can be assembled from the same alphabet rather than from a second copy of it that could drift.

ASCII only. Admitting the full Unicode letter category would let two names that render identically — homoglyphs, or the same string in NFC and NFD — claim to be two different tables.

View Source
var ErrInvalidIdentifier = platformerrors.New("invalid SQL identifier")

ErrInvalidIdentifier indicates a name that ValidIdentifier rejects. Packages wrap it with their own context, so errors.Is works across all of them — including across a package that builds a table's DDL and one that queries it, which is the pair most likely to be checked against each other.

View Source
var ErrUnsupported = platformerrors.New("unsupported SQL dialect")

ErrUnsupported indicates a dialect outside the supported set. Packages wrap it with their own context, so errors.Is works across all of them.

Functions

func RequireDialect

func RequireDialect(component string, d Dialect, want ...Dialect) error

RequireDialect returns a wrapped ErrUnsupported naming component and d unless d is one of want.

It is for the packages whose SQL is written against particular dialects rather than reduced to a portable subset, so that all of them refuse the same way and at the same moment — construction — instead of emitting syntax the server rejects on the first query. component names the caller in the message ("work queue", "workqueue migration"), since a process wiring several of these needs to know which one objected.

want is variadic because the constraint is not always a single dialect: this module holds packages that support Postgres and MySQL but not SQLite, which has no SKIP LOCKED. Prefer Valid over listing all three.

Calling it with no accepted dialects is a programming error rather than a vacuous pass, and says so.

func RequirePostgres

func RequirePostgres(component string, d Dialect) error

RequirePostgres is RequireDialect for the Postgres-only packages, which are the common case — the work queue and its migrations both reach for it.

func SplitStatements

func SplitStatements(ddl string) []string

SplitStatements strips '--' comments from ddl and splits it into individually executable statements on ';', preserving statement order.

Comments come out before the split, not after. A '--' comment may contain a semicolon — prose routinely does — and splitting first tears such a comment in half, leaving its tail masquerading as SQL at the head of the next statement.

Comment stripping handles whole-line '--' comments and blank lines only, not a '--' appearing after SQL on the same line, nor semicolons inside string literals; the DDL shipped by this module contains neither, and the round-trip tests against real servers are what keep that true.

func ValidIdentifier

func ValidIdentifier(s string) bool

ValidIdentifier reports whether s is safe to interpolate into query text as a table name. Table names are interpolated rather than bound, so they are restricted rather than escaped.

Types

type Dialect

type Dialect string

Dialect selects the SQL a package emits. It must match the database provider the emitted SQL runs against.

const (
	// Postgres targets PostgreSQL, which numbers its placeholders and supports
	// SKIP LOCKED.
	Postgres Dialect = "postgres"
	// MySQL targets MySQL 8.0+ — the first version with WITH RECURSIVE — which
	// supports SKIP LOCKED.
	MySQL Dialect = "mysql"
	// SQLite targets SQLite, which is single-writer by nature and has no
	// SKIP LOCKED.
	SQLite Dialect = "sqlite"
)

func (Dialect) Placeholder

func (d Dialect) Placeholder(n int) string

Placeholder renders the n-th bind marker (1-indexed). Postgres numbers its placeholders; MySQL and SQLite do not.

func (Dialect) Placeholders

func (d Dialect) Placeholders(start, count int) string

Placeholders renders count bind markers starting at start, joined for use inside an IN clause or a VALUES tuple.

func (Dialect) QuoteIdentifier

func (d Dialect) QuoteIdentifier(id string) string

QuoteIdentifier renders an identifier as a quoted one for d, doubling any embedded quote character so it cannot end the quoting early.

It is the escaping counterpart to ValidIdentifier's restricting, and both exist because a table or column name is interpolated into statement text rather than bound. Prefer ValidIdentifier where the name comes from configuration and a rejection is actionable; this is for the names that are legal-but-awkward — a mixed-case column, a reserved word — where refusing would be refusing a database somebody already has.

Postgres and SQLite quote with double-quotes per the SQL standard; MySQL quotes with backticks. An unrecognized dialect gets the standard form, which is the one every dialect here but MySQL uses.

This is not sanitization for arbitrary input. A NUL byte, or a name from a hostile source, still belongs in ValidIdentifier's hands: doubling the quote character makes a legal identifier safe to quote, not an arbitrary string safe to interpolate.

func (Dialect) SupportsNotify

func (d Dialect) SupportsNotify() bool

SupportsNotify reports whether the dialect can signal a listening session with LISTEN/NOTIFY, which is what lets a poller be woken instead of waiting out its interval.

func (Dialect) SupportsSkipLocked

func (d Dialect) SupportsSkipLocked() bool

SupportsSkipLocked reports whether the dialect can claim rows with FOR UPDATE SKIP LOCKED, which is what allows more than one competing worker to claim from the same table at once.

func (Dialect) Valid

func (d Dialect) Valid() bool

Valid reports whether d is a dialect this module can emit SQL for.

Jump to

Keyboard shortcuts

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