dialect

package
v0.0.0-...-603fb30 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package dialect defines the driver interfaces separating the dialect-agnostic core from database specifics. v0.1 ships only the LexerProfile half; Frontend/Oracle land in P2/P4.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EntryFromDesc

func EntryFromDesc(fp, renderedSQL string, d Desc) *cache.OracleEntry

EntryFromDesc converts a Describe answer into its committed-cache form. This is the single Desc→entry conversion: the pipeline and the oracle corpus harness must serialize identically, or cache byte-identity across oracle backends (design 15 §3) stops being checkable.

func FoldIdent

func FoldIdent(profile LexerProfile) func(string) string

FoldIdent returns the profile's identifier-folding function: on a case-insensitive dialect it maps an identifier to its case-folded form, elsewhere it is the identity. The resolution-based rules (R3 guard scope, R2 qualified star) run every qualifier, alias, scope name, and catalog column name through it so a mixed-case reference cannot slip past a guard check on a dialect that would resolve it at runtime (SQLETCH115/117). Fold both sides of every comparison.

func InEmptyOf

func InEmptyOf(profile LexerProfile) string

InEmptyOf resolves a profile's arity-0 @in emission.

func ShiftOracleErrPos

func ShiftOracleErrPos(err error, delta int) error

ShiftOracleErrPos returns err with an *OracleError's Pos moved left by delta bytes. The plan oracles measure error positions against an EXPLAIN-wrapped string (a delta-byte prefix + the rendering), but pipeline diagnostics map Pos straight through the rendering's source map, so the prefix must be stripped for the position to be rendering-relative like the Describe path's. A Pos landing inside the stripped prefix, or a non-positional error, is reported unpositioned (Pos = -1) rather than mis-attributed; a non-*OracleError is returned unchanged.

Types

type CTEDef

type CTEDef struct {
	Name      string
	Recursive bool
	// Tree is the facade over the body, nil when the body is not a
	// plain query (a data-modifying CTE): such bodies expose only
	// RETURNING rows via a target list the engine attributes to the
	// base tables the DML reads.
	Tree Tree
	// PoisonTables lists every base-table name a data-modifying body
	// (nil Tree) mentions. PostgreSQL attributes a wCTE column through
	// GetCTETargetList — the RETURNING list — to a base table's OID,
	// and that table may sit on a null-extended side of a join inside
	// the DML (e.g. RETURNING a RIGHT JOIN's null-extended side). The
	// analyzer must POISON these OIDs so no clean OUTER instance of the
	// same table can vouch for the null-extended provenance (design 05
	// §2b). Populated only by the PostgreSQL facade — MySQL/SQLite CTE
	// bodies are SELECT-only, so it stays nil there.
	PoisonTables []TableRef
}

CTEDef is one WITH-list definition.

type CaseInsensitiveIdents

type CaseInsensitiveIdents interface {
	CaseInsensitiveIdents() bool
}

CaseInsensitiveIdents is implemented by lexer profiles whose dialect resolves identifier references case-insensitively: an alias written `A` is matched by a reference `a`, and a column `Kind` by `kind` (MySQL, SQLite). PostgreSQL case-folds unquoted identifiers at parse time, so its facade already yields folded names — it leaves this unimplemented and folding is then the identity.

type ColRef

type ColRef struct {
	Fields     []string // qualified parts, e.g. ["u", "id"] or ["id"]
	Star       bool     // reference ends in * (e.g. u.*)
	Loc        int      // byte offset in the parsed SQL
	InSubquery bool     // inside a sublink/derived table/CTE scope
	// ScopeAliases is the set of effective relation names (alias if
	// present, else table name) introduced by the subquery scope(s)
	// ENCLOSING this reference — the union across every enclosing
	// subquery level, but NOT the top-level statement's own FROM. It is
	// nil for a top-level reference. R3 uses it to resolve a qualified
	// reference innermost-first: a qualifier found here is bound by a
	// nearer scope (SQL resolves innermost-first), so the reference does
	// not touch a same-named top-level relation and its guard need not
	// be re-derived. A correlated reference — whose qualifier is a
	// top-level relation absent from every enclosing subquery FROM — is
	// NOT listed here and is still checked. Facades under-collect rather
	// than over-collect (set-operation branch FROMs are omitted): a
	// missing name only preserves the pre-existing (sound) check, an
	// extra name would wrongly suppress one.
	ScopeAliases []string
}

ColRef is one column reference anywhere in the statement.

type ColumnDesc

type ColumnDesc struct {
	Name   string
	Type   TypeRef
	SrcRel uint32
	SrcAtt int16
}

ColumnDesc describes one result column. SrcRel/SrcAtt tie a direct column reference back to its catalog column (0 when the column is a computed expression) — the nullability analysis keys on this.

type Desc

type Desc struct {
	Params  []TypeRef // by placeholder position ($1 = [0])
	Columns []ColumnDesc
}

Desc is the oracle's answer for one rendering.

func DescFromEntry

func DescFromEntry(e *cache.OracleEntry) Desc

DescFromEntry is EntryFromDesc's inverse, used on cache hits.

type Frontend

type Frontend interface {
	Parse(sql string) (Tree, error)
	ProbeExpr(expr string) error
	ProbeJoinItem(item string) error
	ProbeOrderBy(clause string) error
	ProbeOrderByKey(expr string) error
	ProbeGroupBy(clause string) error
	ProbeSetItem(item string) error
	ProbeInsertValue(expr string) error
}

Frontend parses rendered SQL and answers node-completeness probes (design 02 §4). Probe methods return nil when the fragment forms exactly one complete node of its slot.

type GoTypeRef

type GoTypeRef struct {
	Name   string
	Import string
}

GoTypeRef is a Go type for a database type, plus the import it needs ("" for builtins).

type InEmpty

type InEmpty interface {
	InEmptySQL() string
}

InEmpty is implemented by expanding-dialect profiles to provide the arity-0 @in emission: a fragment completing `expr <here>` so that an empty list matches nothing — FALSE even for a NULL operand, matching PostgreSQL's `= ANY('{}')`.

type JoinType

type JoinType int

JoinType classifies how a FROM relation is introduced.

const (
	JoinBase JoinType = iota // plain FROM item, not a join
	JoinInner
	JoinLeft
	JoinRight
	JoinFull
	JoinCross
	JoinOther
)

func (JoinType) String

func (j JoinType) String() string

type LexError

type LexError struct {
	Pos int
	Msg string
}

LexError is returned for unterminated strings/comments; the scanner converts it into a diagnostic at Pos.

func (*LexError) Error

func (e *LexError) Error() string

type LexerProfile

type LexerProfile interface {
	// NextToken lexes the token starting at src[pos:]. pos is
	// guaranteed to be a token boundary. At end of input it returns
	// a token with Kind == KindEOF.
	NextToken(src []byte, pos int) (Token, error)
}

LexerProfile lets the shared template scanner walk dialect SQL without parsing it. Implementations only need correct token *boundaries* (strings, comments, params, operators), not SQL understanding.

type NativeUnsupportedError

type NativeUnsupportedError struct {
	Pos       int    // byte offset into the described SQL (-1 unknown)
	Construct string // what was refused, human-readable
	Hint      string // the compliant rewrite, if one exists
}

NativeUnsupportedError is a native-backend refusal: the input is outside the backend's modeled subset (design 15 §2 — refuse, never guess). Distinct from OracleError, which mirrors a rejection the real engine would also make; the CLI maps this to SQLETCH214 and the differential harness treats it as the tolerable direction only.

func (*NativeUnsupportedError) Error

func (e *NativeUnsupportedError) Error() string

type Oracle

type Oracle interface {
	// Describe prepares (never executes) sql and reports parameter and
	// result column types.
	Describe(ctx context.Context, sql string) (Desc, error)
	// Plan runs the dialect's plan-only statement (EXPLAIN) to surface
	// planner-stage errors that prepare cannot see.
	Plan(ctx context.Context, sql string) error
	// Snapshot dumps the catalog portions needed for offline analysis.
	Snapshot(ctx context.Context) (*cache.Catalog, error)
	ServerVersion(ctx context.Context) (string, error)
}

Oracle is the type oracle: it answers what the database itself knows about a rendering. Backends (server, embedded engine, native inference) implement the same interface — see the Oracle backends section of docs/spec.md.

type OracleError

type OracleError struct {
	Pos           int
	SQLState      string
	Msg           string
	Indeterminate bool
}

OracleError reports a prepare/describe failure. Pos is a byte offset into the described SQL (-1 when unknown). Indeterminate is set for "could not determine data type of parameter" failures — the CLI attaches the explicit-cast hint (SQLETCH201).

func (*OracleError) Error

func (e *OracleError) Error() string

type ParseError

type ParseError struct {
	Pos int
	Msg string
}

ParseError reports a dialect parse failure at a byte offset into the parsed SQL (rendered text; callers map it back to the template).

func (*ParseError) Error

func (e *ParseError) Error() string

type PlaceholderStyle

type PlaceholderStyle int

PlaceholderStyle is how a dialect binds parameters in prepared SQL.

const (
	// PlaceholderDollar: $1, $2, … numbered in first-occurrence order;
	// repeated references to one bind source reuse the number
	// (PostgreSQL).
	PlaceholderDollar PlaceholderStyle = iota
	// PlaceholderQuestion: '?', one placeholder per occurrence;
	// repeated references repeat the bind (MySQL, SQLite).
	PlaceholderQuestion
)

func StyleOf

func StyleOf(profile LexerProfile) PlaceholderStyle

StyleOf resolves a profile's placeholder style.

type Placeholders

type Placeholders interface {
	PlaceholderStyle() PlaceholderStyle
}

Placeholders is implemented by lexer profiles to declare their dialect's bind-placeholder style. Profiles that do not implement it default to PlaceholderDollar.

type RelRef

type RelRef struct {
	Alias  string // alias if present, else ""
	Table  string // relation name ("" for subselects etc.)
	Schema string // explicit schema/database qualifier, "" when unqualified
	// Only marks a `FROM ONLY table` reference (PostgreSQL): the scan
	// excludes inheritance children, so child rows cannot undermine
	// the parent's NOT NULL declarations.
	Only bool
	Loc  int // byte offset of the relation in the parsed SQL
	Join JoinType
	// NullableSide reports whether this relation sits on a
	// null-extended side of an outer join in this statement (right of
	// LEFT, left of RIGHT, either side of FULL) — the nullability
	// analysis input.
	NullableSide bool
}

RelRef is one relation of the statement's FROM/target clauses.

type StmtKind

type StmtKind int

StmtKind is the statement class sqletch supports.

const (
	StmtOther StmtKind = iota
	StmtSelect
	StmtUpdate
	StmtInsert
	StmtDelete
)

type SubRel

type SubRel struct {
	Alias        string
	NullableSide bool // on a null-extended side of the ENCLOSING level
	Tree         Tree
}

SubRel is one FROM-reachable derived table, wrapped in its own Tree facade so the nullability analyzer can recurse instead of distrusting the whole statement (design 05 §2b).

type TableRef

type TableRef struct {
	Name   string
	Schema string // explicit schema/database qualifier, "" when unqualified
	Loc    int
}

TableRef is one base-table name referenced anywhere in the statement, including subquery and CTE bodies — the policy weaver's visibility input (design 14 §11.1) and the nullability analyzer's poisoning input (design 05 §2b). References to CTE *names* are deliberately included: a CTE shadowing a policy-designated table is conservatively treated as touching it (a false positive is a loud diagnostic with an opt-out, never a silent leak). Loc is -1 where the parser exposes no offset.

type TargetItem

type TargetItem struct {
	Name      string // output alias ("" if none)
	Star      bool   // item is * or qualifier.*
	Qualifier string // "u" for u.*; "" for bare *
	FuncName  string // lowercased function name when the item is a bare call
	// Total marks an expression that can never evaluate to NULL
	// regardless of data: a non-NULL literal, EXISTS, an IS [NOT]
	// NULL / boolean test, a non-null value function, a cast of a
	// total expression, or coalesce with at least one total argument.
	// Data-INDEPENDENT only — a column reference never makes an
	// expression total (its nullability is the analyzer's job).
	Total bool
	// AggArg is the qualified path of the call's single bare-column
	// argument (["u","org_id"] or ["org_id"]) when the item is a
	// plain aggregate call over exactly one column with no FILTER
	// clause and no OVER clause; nil otherwise. The nullability
	// analyzer combines it with GROUP BY presence: an aggregate over
	// a non-nullable column of a non-null-extended relation is
	// non-null when every output row's group is non-empty.
	AggArg []string
	Loc    int
}

TargetItem is one projection entry.

type Token

type Token struct {
	Kind  TokenKind
	Start int // byte offset into src, inclusive
	End   int // exclusive
	Text  string
}

type TokenKind

type TokenKind int
const (
	KindEOF TokenKind = iota
	KindWhitespace
	KindLineComment
	KindBlockComment
	KindString      // includes dollar-quoted and E” strings
	KindQuotedIdent // "ident"
	KindIdent
	KindNumber
	KindParamRef        // :name (Text includes the colon)
	KindPositionalParam // $1, $2, …
	KindCast            // ::
	KindOperator
	KindLParen
	KindRParen
	KindComma
	KindSemicolon
	KindOther // ., [, ], and anything else structurally irrelevant
)

type Tree

type Tree interface {
	StmtCount() int
	Kind() StmtKind
	Relations() []RelRef
	// DeepTables reports every base-table name referenced anywhere in
	// the statement — subqueries and CTE bodies included, unlike
	// Relations, which stops at the statement's own FROM/target
	// clauses. The policy weaver compares the two to reject designated
	// tables in positions it cannot scope (design 14 §D6).
	DeepTables() []TableRef
	ColumnRefs() []ColRef
	TargetItems() []TargetItem
	// TopConjunctLocs returns the byte locations of the statement's
	// top-level WHERE conjuncts (AND-flattened).
	TopConjunctLocs() []int
	// HavingConjunctLocs is TopConjunctLocs for the statement-level
	// HAVING clause (empty when the statement has none).
	HavingConjunctLocs() []int
	// OrderByLocs returns byte locations of statement-level ORDER BY
	// item expressions.
	OrderByLocs() []int
	HasDistinctOn() bool
	HasLockingClause() bool
	// HasFetchWithTies reports FETCH FIRST … WITH TIES, which makes
	// the ORDER BY clause mandatory (@order-by then needs a @default).
	HasFetchWithTies() bool
	// HasSetOperation reports a statement-level set operation
	// (UNION/INTERSECT/EXCEPT, SQLite compound selects). Engines may
	// attribute set-op output to a branch's base table (SQLite: the
	// FIRST branch), which no per-branch analysis can license —
	// SrcRel narrowing is off for the level (design 05 §2b).
	HasSetOperation() bool
	// DerivedRels returns the statement's own FROM-reachable derived
	// tables, each wrapped in a sub-facade for recursive analysis.
	DerivedRels() []SubRel
	// CTEs returns the statement's WITH-list definitions in order.
	CTEs() []CTEDef
	// HasUnresolvableProvenance reports that the ORACLE's column
	// attribution for this statement can cross-resolve to a wrong
	// catalog entry: MySQL/SQLite attribute by BARE table name with
	// no database qualifier, so any db-qualified reference anywhere
	// in the statement (subqueries included) poisons every
	// name-keyed attribution. PostgreSQL attributes by OID and always
	// reports false (design 05 §2a).
	HasUnresolvableProvenance() bool
	// HasGroupingSets reports ROLLUP / CUBE / GROUPING SETS in the
	// statement-level GROUP BY: super-aggregate rows null out grouping
	// columns regardless of catalog NOT NULL, so SrcRel-based
	// narrowing is unsound while one is present (design 05 §2a).
	HasGroupingSets() bool
	// HasGroupBy reports a statement-level GROUP BY clause (of any
	// form). With one present — and no grouping sets — every output
	// row aggregates a NON-EMPTY group, which is what lets strict
	// aggregates over non-nullable columns narrow (design 05 §3a).
	HasGroupBy() bool
	// NotNullConjuncts returns the bare column reference of every
	// depth-0 statement-level WHERE conjunct of the exact form
	// `col IS NOT NULL`. Loc is the conjunct's byte offset in the
	// parsed SQL — the analyzer uses it to require the conjunct to be
	// SKELETON text (present in every shape) before narrowing.
	NotNullConjuncts() []ColRef
	// HasConflictUpdate reports an INSERT whose conflict arm MODIFIES
	// rows — PostgreSQL/SQLite `ON CONFLICT … DO UPDATE`, MySQL
	// `ON DUPLICATE KEY UPDATE`. A `DO NOTHING` arm and every non-INSERT
	// statement report false. The policy weaver refuses to weave such an
	// upsert on a designated table (design 14 §D6, owner decision
	// 2026-08-21): the DO UPDATE arm rewrites rows but cannot carry a
	// woven WHERE that scopes the conflict, so refusal is the sound
	// minimum.
	HasConflictUpdate() bool
}

Tree is the narrow dialect-AST facade the rules engine consumes. Deliberately minimal: extending it is a compile-visible act.

type TypeMap

type TypeMap interface {
	GoType(oid uint32) (GoTypeRef, bool)
}

TypeMap translates database types to Go types for codegen.

type TypeRef

type TypeRef struct {
	OID  uint32
	Name string
}

TypeRef identifies a database type.

Directories

Path Synopsis
Package mysql implements the MySQL dialect driver (Tier 2).
Package mysql implements the MySQL dialect driver (Tier 2).
Package postgres implements the PostgreSQL dialect driver.
Package postgres implements the PostgreSQL dialect driver.
Package sqlite implements the SQLite dialect driver (Tier 2).
Package sqlite implements the SQLite dialect driver (Tier 2).

Jump to

Keyboard shortcuts

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