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
- func EntryFromDesc(fp, renderedSQL string, d Desc) *cache.OracleEntry
- func FoldIdent(profile LexerProfile) func(string) string
- func InEmptyOf(profile LexerProfile) string
- func PipesAreOr(profile LexerProfile) bool
- func ShiftOracleErrPos(err error, delta int) error
- type CTEDef
- type CaseInsensitiveIdents
- type ColRef
- type ColumnDesc
- type ConflictDeleter
- type Desc
- type Frontend
- type GoTypeRef
- type InEmpty
- type JoinType
- type LexError
- type LexerProfile
- type NativeUnsupportedError
- type NestedDML
- type Oracle
- type OracleError
- type ParseError
- type PipesOr
- type PlaceholderStyle
- type Placeholders
- type RelRef
- type StmtKind
- type SubRel
- type TableRef
- type TargetItem
- type Token
- type TokenKind
- type Tree
- type TypeExt
- type TypeMap
- type TypeRef
- type ValueTarget
Constants ¶
const TypeKindEnum = "e"
TypeKindEnum is TypeExt.Kind for an enum.
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 PipesAreOr ¶ added in v0.0.9
func PipesAreOr(profile LexerProfile) bool
PipesAreOr reports whether profile's dialect parses `||` as logical OR.
func ShiftOracleErrPos ¶
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 ¶
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 ConflictDeleter ¶ added in v0.0.9
type ConflictDeleter interface {
HasConflictDelete() bool
}
ConflictDeleter is an optional Tree capability: HasConflictDelete reports a statement whose conflict handling DELETES an existing row (MySQL `REPLACE`, F10; SQLite `INSERT OR REPLACE` / `REPLACE INTO` / `UPDATE OR REPLACE`, F17). The policy weaver then governs the statement by `delete` coverage in addition to the `update` coverage HasConflictUpdate already triggers (design 14 §5, upsert refinement). A Tree that does not implement it has no deleting conflict form.
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 ¶
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 LexError ¶
LexError is returned for unterminated strings/comments; the scanner converts it into a diagnostic at Pos.
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 NestedDML ¶ added in v0.0.9
type NestedDML struct {
Kind StmtKind // StmtUpdate, StmtDelete, or StmtInsert
Table string // target relation name
Schema string // explicit schema qualifier, "" when unqualified
// Upsert marks a nested INSERT whose conflict arm modifies rows
// (ON CONFLICT … DO UPDATE) — an update-shaped modification.
Upsert bool
Loc int // byte offset of the target relation in the parsed SQL
}
NestedDML is one data-modifying statement nested inside the parsed statement (see Tree.NestedDML).
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 ¶
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 ¶
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 PipesOr ¶ added in v0.0.9
type PipesOr interface {
PipesOr() bool
}
PipesOr is implemented by lexer profiles whose dialect parses the `||` operator as logical OR — binding LOOSER than AND — under its default configuration (MySQL: unless sql_mode has PIPES_AS_CONCAT). Clause scanners that decide whether an appended conjunct needs parentheses treat such an operator exactly like OR. On PostgreSQL and SQLite `||` is string concatenation (tighter than AND), so they leave it unimplemented. Over-reporting is safe (an extra wrap); under-reporting is a tenant leak in policy weaving.
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 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
// Sublink marks a projection item that IS a scalar subquery
// (`(SELECT …)`). Such a column can be NULL when the subquery matches
// no rows, regardless of the subquery column's catalog nullability.
// PostgreSQL/MySQL report no base-table provenance for it (SrcRel=0),
// but SQLite's sqlite3_column_origin_name resolves THROUGH the sublink
// to the base column, so the analyzer must not trust that provenance
// for a Sublink item (audit-19).
Sublink bool
Loc int
}
TargetItem is one projection entry.
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
// SetOpBranches returns the leaf query cores of a statement-level
// set operation in document order — nested and parenthesized
// operands flattened — each as a sub-facade (Kind StmtSelect) whose
// Relations are that core's own FROM relations, located (Loc) in
// the ORIGINAL parsed SQL, and whose DeepTables are everything the
// core reads. Clauses owned by the whole set operation (its WITH,
// ORDER BY, LIMIT) belong to no branch; a parenthesized operand's
// own clauses belong to its branch. nil when the statement is not
// a set operation. The policy weaver scopes every branch
// independently (design 14 §13); it cross-checks this list against
// its own lexical branch split and refuses on any disagreement.
SetOpBranches() []Tree
// 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 a statement whose conflict handling
// MODIFIES an existing row — PostgreSQL/SQLite `ON CONFLICT … DO
// UPDATE`, MySQL `ON DUPLICATE KEY UPDATE`, MySQL `REPLACE`, and
// SQLite's REPLACE algorithm (`INSERT OR REPLACE`, `REPLACE INTO`,
// `UPDATE OR REPLACE`); the REPLACE forms delete the colliding row
// (such a facade also implements ConflictDeleter). A `DO NOTHING`
// arm, `INSERT IGNORE`, and every other 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
// NestedDML reports every data-modifying statement nested inside
// this one — PostgreSQL's data-modifying CTEs (`WITH d AS (DELETE
// … RETURNING …) …`), at any depth — as (kind, target table)
// pairs, in document order. An INSERT whose conflict arm modifies
// rows (`ON CONFLICT … DO UPDATE`) is reported with Upsert set; a
// plain nested INSERT is reported with Upsert false. The top-level
// statement itself is never listed. The policy weaver refuses a
// designated target of a nested UPDATE/DELETE/upsert whose kind the
// policy covers, whatever the outer statement's kind (design 14
// §D6, owner decision 2026-10-21). MySQL and SQLite admit only
// SELECT bodies in WITH, so their facades report nothing.
NestedDML() []NestedDML
// ValueTargets reports every direct value position (design 20 §3.1)
// of the statement: a placeholder that is the whole value of an
// INSERT VALUES item paired with an explicit column list, or of a
// single-column UPDATE SET item — looking through explicit casts and
// parentheses, never through any other expression. Conflict arms,
// INSERT … SELECT/SET, tuple and subfield assignments, and every
// other clause are not listed. Facades under-report rather than
// over-report: an unlisted position keeps its parameter a plain
// value, while a wrongly listed one could make it nullable.
ValueTargets() []ValueTarget
}
Tree is the narrow dialect-AST facade the rules engine consumes. Deliberately minimal: extending it is a compile-visible act.
type TypeExt ¶ added in v0.0.8
type TypeExt struct {
Schema string
// Kind is pg_type.typtype: "e" enum, "b" base (an array of an enum,
// an extension type), "d" domain, "c" composite, "r" range, …
Kind string
// Labels are an enum's labels in enumsortorder; empty otherwise.
Labels []string
}
TypeExt is the oracle's description of a user-defined type.
type TypeRef ¶
type TypeRef struct {
OID uint32
Name string
// Ext describes a user-defined type (design 26 §4.1). Only the
// PostgreSQL oracle sets it — for EVERY OID >= 16384 and for no
// built-in — so "user-defined and no Ext" can only be an oracle
// entry written before Ext existed (D4).
Ext *TypeExt
}
TypeRef identifies a database type.
type ValueTarget ¶ added in v0.0.3
type ValueTarget struct {
Column string
// Qualifier is the table/alias qualifier written on the target
// column (MySQL `SET u.nickname = ?`), "" when unqualified.
Qualifier string
Loc int
}
ValueTarget is one direct value position: the placeholder at Loc (a byte offset in the parsed SQL) is the entire value written into Column of the statement's target relation.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package dialecttest holds cross-dialect conformance helpers shared by the dialect packages' tests.
|
Package dialecttest holds cross-dialect conformance helpers shared by the dialect packages' tests. |
|
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). |