Documentation
¶
Overview ¶
Package semantic is the Go port of Java's `com.apple.foundationdb.relational.recordlayer.query.SemanticAnalyzer` plus related Identifier / Expression / reference-resolution helpers.
The Java class is a 1280-line monolith doing identifier normalization, table / CTE / function lookup, index validation, ORDER BY validation, type inference, star expansion, nested-field resolution, and correlated-identifier resolution. The Go port breaks that up across focused files:
- identifier.go — Identifier type + case-folding normalization.
- (future) — table / column / CTE / index lookup, type inference, star expansion.
Currently ships only the identifier machinery; the resolution + type passes are follow-up work.
Index ¶
- func NormalizeString(s string, caseSensitive bool) string
- type AmbiguousColumnError
- type Analyzer
- func (a *Analyzer) BuildScopeFromFromClause(parent *Scope, fromCtx antlrgen.IFromClauseContext) (*Scope, error)
- func (a *Analyzer) CaseSensitive() bool
- func (a *Analyzer) Catalog() Catalog
- func (a *Analyzer) ExpandQualifiedStar(scope *Scope, qualifier Identifier) ([]ExpandedColumn, error)
- func (a *Analyzer) ExpandScopeStar(scope *Scope) []ExpandedColumn
- func (a *Analyzer) ExpandStar(table Table) []Column
- func (a *Analyzer) ResolveColumn(table Table, id Identifier) (Column, error)
- func (a *Analyzer) ResolveColumnRef(scope *Scope, qualifier, id Identifier) (Column, ScopeSource, error)
- func (a *Analyzer) ResolveTable(name QualifiedName) (Table, error)
- func (a *Analyzer) ResolveTableRef(ctx antlrgen.IFullIdContext) (Table, error)
- type Catalog
- type Column
- type ColumnNotFoundError
- type DuplicateAliasError
- type ExpandedColumn
- type FunctionArityError
- type FunctionCatalog
- type FunctionKind
- type FunctionNotFoundError
- type FunctionSpec
- type Identifier
- type InMemoryCatalog
- type QualifiedName
- func (q QualifiedName) EqualsIgnoreQuoting(other QualifiedName) bool
- func (q QualifiedName) IsQualified() bool
- func (q QualifiedName) IsZero() bool
- func (q QualifiedName) LeafIdentifier() Identifier
- func (q QualifiedName) Name() string
- func (q QualifiedName) PrefixedWith(prefix QualifiedName) bool
- func (q QualifiedName) Qualifier() []string
- func (q QualifiedName) Segments() []string
- func (q QualifiedName) String() string
- type Scope
- func (s *Scope) AddSource(src ScopeSource) error
- func (s *Scope) AllSourcesRecursive() []ScopeSource
- func (s *Scope) Parent() *Scope
- func (s *Scope) ResolveColumn(id Identifier) (Column, ScopeSource, error)
- func (s *Scope) ResolveQualifiedColumn(qualifier, col Identifier) (Column, ScopeSource, error)
- func (s *Scope) Sources() []ScopeSource
- type ScopeSource
- type SourceNotFoundError
- type StaticTable
- type Table
- type TableNotFoundError
- type UnsupportedFromShapeError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NormalizeString ¶
NormalizeString is the lower-level helper underlying New. Exposed for call sites that have a raw string and don't need the full Identifier wrapper (e.g. dedup keys in the parser). Mirrors Java's `SemanticAnalyzer.normalizeString`.
- Empty string → empty string.
- Quoted (single or double) → strip quotes verbatim.
- Unquoted + caseSensitive → return unchanged.
- Unquoted + !caseSensitive → upper-case.
Types ¶
type AmbiguousColumnError ¶
type AmbiguousColumnError struct {
Id Identifier
// Matches is always equal to len(Sources); exists as a
// convenience accessor for callers who don't need the full
// alias list. Future API tightening may remove it — prefer
// len(Sources) for new code.
Matches int
// Sources is the list of ScopeSource aliases that matched,
// allowing the user-facing message to suggest
// `alias.column` for each candidate.
Sources []Identifier
}
AmbiguousColumnError is returned when a bare column reference matches multiple sources at the same scope level. Carries the conflicting identifier and the conflicting source aliases so the user knows which tables to qualify against.
func (*AmbiguousColumnError) Error ¶
func (e *AmbiguousColumnError) Error() string
type Analyzer ¶
type Analyzer struct {
// contains filtered or unexported fields
}
Analyzer ties Catalog lookups + identifier normalization into the resolution helpers that rule authors / logical-plan builders invoke. Mirrors the instance surface of Java's `SemanticAnalyzer` (the static methods on that class — case folding etc. — live as free functions in this package).
Seed scope: resolve table references, resolve bare/qualified column references. Star expansion, nested-field lookup, correlated-identifier resolution all land in follow-up shifts.
Not safe for concurrent mutation of the underlying Catalog; the Analyzer itself is stateless once constructed.
func NewAnalyzer ¶
NewAnalyzer wires up an Analyzer against the given catalog. A nil catalog is rejected — callers who don't have a real schema yet should use NewInMemoryCatalog() with no tables.
func (*Analyzer) BuildScopeFromFromClause ¶
func (a *Analyzer) BuildScopeFromFromClause(parent *Scope, fromCtx antlrgen.IFromClauseContext) (*Scope, error)
BuildScopeFromFromClause walks a parsed FROM clause and produces a Scope populated with one ScopeSource per TableSource. The analyzer resolves each table via its catalog; missing tables return the first TableNotFoundError encountered.
Supports the simple shape (comma-separated AtomTableItem entries + optional alias); subquery-in-FROM and JOIN clauses are deferred until the join/derived-table resolution passes land. Unsupported shapes return an UnsupportedFromShapeError so callers can fall back to the existing logical-builder path cleanly.
Pass parent=nil for a top-level query; pass the enclosing scope for correlated subqueries.
func (*Analyzer) CaseSensitive ¶
CaseSensitive reports the analyzer's case-sensitivity setting.
func (*Analyzer) Catalog ¶
Catalog returns the underlying Catalog. Exposed so higher-level passes (e.g. LogicalPlan builder) can thread the same catalog through without re-wiring.
func (*Analyzer) ExpandQualifiedStar ¶
func (a *Analyzer) ExpandQualifiedStar(scope *Scope, qualifier Identifier) ([]ExpandedColumn, error)
ExpandQualifiedStar implements `SELECT alias.*` against a Scope: looks up the named source, then its columns. Walks the parent chain for correlated-star references. Returns SourceNotFoundError (with the Available alias list populated from every visible scope for "did you mean?" rendering) when no source matches.
func (*Analyzer) ExpandScopeStar ¶
func (a *Analyzer) ExpandScopeStar(scope *Scope) []ExpandedColumn
ExpandScopeStar implements unqualified `SELECT *` against a Scope: concatenates each source's columns in FROM-order. Ambiguity is NOT flagged here — Java's SQL lets two sources expose same-named columns through `SELECT *` (the output just gets two columns); only bare *references* error. Downstream callers tag each ExpandedColumn with its source so later projection rewrites can qualify.
func (*Analyzer) ExpandStar ¶
ExpandStar implements the `SELECT *` rewrite — returns the full column list of the given table in declared order. Each Column is returned unchanged (same Id, Type, Nullable) so downstream plan builders can wrap each into a ColumnReference / ProjectionItem.
Mirrors the single-qualifier case of Java's `SemanticAnalyzer.expandStar`. The multi-table / alias-qualified cases (`SELECT t.* FROM t JOIN u`) come with the FROM-scope port.
func (*Analyzer) ResolveColumn ¶
func (a *Analyzer) ResolveColumn(table Table, id Identifier) (Column, error)
ResolveColumn looks up a column by identifier against a resolved table. Mirrors the simple case of Java's resolveIdentifier — qualifier resolution (`t.col` → column on aliased table) comes later with the FROM-clause scope machinery.
func (*Analyzer) ResolveColumnRef ¶
func (a *Analyzer) ResolveColumnRef(scope *Scope, qualifier, id Identifier) (Column, ScopeSource, error)
ResolveColumnRef is the one-shot column-reference resolver: given a qualifier (may be zero) and a column identifier, dispatch to bare or qualified lookup against the provided scope. This is the analyzer's top-level hook for every identifier reference the expression resolver sees.
- qualifier.IsZero() → Scope.ResolveColumn (bare). - qualifier non-zero → Scope.ResolveQualifiedColumn.
Returns the same typed errors as the underlying scope methods.
func (*Analyzer) ResolveTable ¶
func (a *Analyzer) ResolveTable(name QualifiedName) (Table, error)
ResolveTable looks up a table by qualified name. Returns a typed error when the name is missing so callers can wrap it into the API-level error shape without string-matching.
func (*Analyzer) ResolveTableRef ¶
func (a *Analyzer) ResolveTableRef(ctx antlrgen.IFullIdContext) (Table, error)
ResolveTableRef is the parse-tree convenience wrapper over ResolveTable. Reads the IFullIdContext (ANTLR's table reference node), builds a QualifiedName with the analyzer's case-sensitivity, then looks it up in the catalog.
Returns TableNotFoundError with the QualifiedName the caller requested; callers preserve user-facing names through `err.Name.String()`.
type Catalog ¶
type Catalog interface {
// LookupTable returns a Table handle for the given qualified
// name, or (nil, false) if no such table exists. Name
// normalization is the caller's job — QualifiedName already
// carries the case-folded form.
LookupTable(name QualifiedName) (Table, bool)
// TableExists reports whether a Table with the given name is
// registered. Equivalent to LookupTable's second return value;
// surfaced separately because it's the common fast-path check
// in existence-gating rules.
TableExists(name QualifiedName) bool
// AllTableNames returns every registered table's qualified
// name. Order is unspecified; callers that need deterministic
// ordering should sort. Used for INFORMATION_SCHEMA-style
// reflection and for error messages that enumerate candidate
// tables.
AllTableNames() []QualifiedName
}
Catalog is the semantic analyzer's view of the schema. The analyzer looks up tables, columns, and indexes through this interface; concrete implementations bridge to `RecordMetaData` (for the embedded engine) or to test fixtures.
Mirrors the subset of Java's `SchemaTemplate` that the Go SemanticAnalyzer port needs. Keeping it narrow so the seed doesn't drag in the full RecordLayer metadata surface — callers who need more adapter methods can extend the Table interface later.
All lookups take QualifiedName so the analyzer can handle schema-qualified references uniformly; concrete impls decide how to resolve un-qualified names (walk the search path, default schema, etc.).
type Column ¶
type Column struct {
// Id is the column name.
Id Identifier
// Type is the column's SQL-ish data type as a string (e.g.
// "INT", "STRING", "BYTES"). Will be replaced with a richer
// `DataType` once the Type hierarchy is ported.
Type string
// Nullable reports whether the column allows NULL values.
// Matters for NOT-NULL-gated simplifications (x = x → TRUE).
Nullable bool
// IsArray reports whether the column is an ARRAY (a repeated proto
// field). The placeholder Type string carries only the scalar/element
// kind, so this is the array signal callers need to type the resolved
// column Value as an ArrayType — e.g. CARDINALITY()'s isArray() check.
// When true, Type is the ELEMENT type string.
IsArray bool
}
Column is the analyzer's view of a table column. Type is a placeholder string until the DataType / Type hierarchy port lands (Phase 4.0 continuation).
type ColumnNotFoundError ¶
type ColumnNotFoundError struct {
TableName QualifiedName
Id Identifier
}
ColumnNotFoundError is returned when ResolveColumn can't find a column on the given table.
func (*ColumnNotFoundError) Error ¶
func (e *ColumnNotFoundError) Error() string
type DuplicateAliasError ¶
type DuplicateAliasError struct {
Alias Identifier
}
DuplicateAliasError is returned by AddSource when the same alias is already registered at this scope level.
func (*DuplicateAliasError) Error ¶
func (e *DuplicateAliasError) Error() string
type ExpandedColumn ¶
type ExpandedColumn struct {
Column Column
Source ScopeSource
}
ExpandedColumn pairs a Column with the ScopeSource it came from. The scope-aware star expander / qualified-star expander returns these so downstream plan builders know which FROM source to attribute each projected column to.
type FunctionArityError ¶
FunctionArityError signals too few / too many arguments.
func (*FunctionArityError) Error ¶
func (e *FunctionArityError) Error() string
type FunctionCatalog ¶
type FunctionCatalog struct {
// contains filtered or unexported fields
}
FunctionCatalog holds the set of functions the analyzer recognizes. Seed ships the core SQL aggregates; scalar function catalogues come as the embedded engine's scalar-function library gets ported.
func NewFunctionCatalog ¶
func NewFunctionCatalog() *FunctionCatalog
NewFunctionCatalog builds an empty catalog. Use RegisterDefaults or Register to populate.
func (*FunctionCatalog) Contains ¶
func (c *FunctionCatalog) Contains(name Identifier) bool
Contains reports whether a function with the given identifier is registered. Equivalent to Lookup's second return.
func (*FunctionCatalog) Lookup ¶
func (c *FunctionCatalog) Lookup(name Identifier) (FunctionSpec, bool)
Lookup returns the FunctionSpec for name (case-insensitive), or (_, false) when not registered.
func (*FunctionCatalog) Register ¶
func (c *FunctionCatalog) Register(spec FunctionSpec) error
Register adds a FunctionSpec. Returns an error on duplicate name; caller can ignore when registering a known stable set or bubble up when the registry is built from user extensions.
func (*FunctionCatalog) RegisterDefaults ¶
func (c *FunctionCatalog) RegisterDefaults()
RegisterDefaults populates the catalog with the standard SQL aggregate functions. Scalar functions are not seeded — they come from the dedicated scalar-function catalogue once that's ported.
type FunctionKind ¶
type FunctionKind int
FunctionKind enumerates the classes of function the analyzer supports.
Values are assigned explicitly (not via `iota`) so inserting a new kind between existing ones doesn't renumber anything — future serialized-plan formats can assume these values are stable.
const ( // FunctionScalar: per-row function (UPPER, LOWER, ABS, etc.). FunctionScalar FunctionKind = 1 // FunctionAggregate: spans multiple rows (COUNT, SUM, MIN, MAX, AVG). FunctionAggregate FunctionKind = 2 )
func (FunctionKind) String ¶
func (k FunctionKind) String() string
String returns the kind as a debug-friendly string.
type FunctionNotFoundError ¶
type FunctionNotFoundError struct {
Name Identifier
}
FunctionNotFoundError signals a lookup miss — the function name isn't registered in the catalogue.
func (*FunctionNotFoundError) Error ¶
func (e *FunctionNotFoundError) Error() string
type FunctionSpec ¶
type FunctionSpec struct {
// Name is the canonical, case-folded function name (e.g. "COUNT").
Name string
// Kind classifies the function — scalar or aggregate (window
// functions come later).
Kind FunctionKind
// MinArgs / MaxArgs bound accepted arity. A MaxArgs of -1 means
// no upper bound (variadic).
MinArgs int
MaxArgs int
// AllowsStar reports whether the function accepts `*` as its
// argument (currently only COUNT does).
AllowsStar bool
// AllowsDistinct reports whether the function accepts a leading
// DISTINCT modifier — e.g. `COUNT(DISTINCT col)`. All aggregates
// in the SQL standard accept DISTINCT; the flag exists here so
// future scalar extensions can opt out.
AllowsDistinct bool
}
FunctionSpec describes a SQL-visible function the analyzer can resolve. The seed differentiates scalar vs aggregate functions since rule-matching / plan-building treats them differently.
func (FunctionSpec) ValidateArity ¶
func (spec FunctionSpec) ValidateArity(argCount int) error
ValidateArity reports whether argCount is acceptable for spec. Zero = no arguments. A MaxArgs of -1 is treated as "no upper bound". Returns a typed error on mismatch so callers can build user-facing messages without string-matching.
Callers handling `*` (star-argument) functions should check `AllowsStar` BEFORE calling ValidateArity and skip the arity check for the star case. The star is syntactically 0 arguments, but COUNT(*) is legal despite COUNT's MinArgs=1 — that's not a contradiction the arity check should reason about.
type Identifier ¶
type Identifier struct {
// contains filtered or unexported fields
}
Identifier is a SQL identifier — a table, column, alias, or function name. Carries the normalized form (case-folded per SQL rules) so Identifiers can live in maps and compare by `==` directly. The original source text is the caller's responsibility (store it alongside via the parse tree's token stream when you need user-spelling-preserving error messages).
Mirrors Java's `com.apple.foundationdb.relational.recordlayer. query.Identifier`, trimmed to the essential equality surface.
func FromUidContext ¶
func FromUidContext(ctx antlrgen.IUidContext, caseSensitive bool) Identifier
FromUidContext converts a single IUidContext to an Identifier. Used for unqualified references (column aliases, CTE names, etc.) where the full-id shape is overkill.
func New ¶
func New(raw string, caseSensitive bool) Identifier
New constructs an Identifier by normalizing raw per SQL rules. When caseSensitive is true, unquoted identifiers retain their source casing; otherwise they're upper-cased. Quoted identifiers always retain case regardless of caseSensitive.
func NewUnquoted ¶
func NewUnquoted(raw string) Identifier
NewUnquoted is the common path — a case-insensitive bare identifier. Equivalent to New(raw, false).
func (Identifier) EqualsIgnoreQuoting ¶
func (i Identifier) EqualsIgnoreQuoting(other Identifier) bool
EqualsIgnoreQuoting compares by Name only, ignoring the quoting flag. For most lookups a quoted-vs-unquoted distinction doesn't matter — e.g. an `ORDER BY` clause referring to `"age"` still targets the same column as a SELECT projecting `age`. Use `==` on Identifier when the quoting distinction matters (resolving against a reserved-word shadow).
func (Identifier) IsZero ¶
func (i Identifier) IsZero() bool
IsZero reports whether i is the zero-value Identifier (empty). Useful for nil-check replacement since Identifier is a value type.
func (Identifier) Name ¶
func (i Identifier) Name() string
Name returns the normalized identifier text. Two Identifiers with the same Name AND same WasQuoted are equal via `==`.
func (Identifier) WasQuoted ¶
func (i Identifier) WasQuoted() bool
WasQuoted reports whether the source text was quoted. Callers that need to preserve user intent (e.g. "keyword" vs keyword) check this flag.
type InMemoryCatalog ¶
type InMemoryCatalog struct {
// contains filtered or unexported fields
}
InMemoryCatalog is a test-friendly Catalog built from a fixed list of tables. Keeps the test surface small — production impls bridge to RecordMetaData; tests construct one of these in a line or two.
func NewInMemoryCatalog ¶
func NewInMemoryCatalog(tables ...Table) *InMemoryCatalog
NewInMemoryCatalog builds a Catalog from the given tables. Table names key the map by their canonical String() form so lookups are O(1).
func (*InMemoryCatalog) AllTableNames ¶
func (c *InMemoryCatalog) AllTableNames() []QualifiedName
AllTableNames implements Catalog. Iterates the map — order is unspecified.
func (*InMemoryCatalog) LookupTable ¶
func (c *InMemoryCatalog) LookupTable(name QualifiedName) (Table, bool)
LookupTable implements Catalog.
func (*InMemoryCatalog) TableExists ¶
func (c *InMemoryCatalog) TableExists(name QualifiedName) bool
TableExists implements Catalog.
type QualifiedName ¶
type QualifiedName struct {
// contains filtered or unexported fields
}
QualifiedName is a dot-separated SQL name like `schema.table.col`. The leaf (last segment) is the "simple name"; the preceding segments are the "qualifier". Both normalized together — callers never see a QualifiedName with one segment upper-cased and another in source case.
Mirrors Java's Identifier-with-qualifier shape. The Go port breaks it off into its own type because:
- Slices aren't comparable, so bundling segments into Identifier would lose the map-as-key ergonomics.
- Most callsites touch only the leaf — an unqualified Identifier stays simple.
Use QualifiedName when you need to preserve the qualifier chain (table aliases, schema-scoped tables). Use Identifier for bare column / alias references.
func FromFullIdContext ¶
func FromFullIdContext(ctx antlrgen.IFullIdContext, caseSensitive bool) QualifiedName
FromFullIdContext converts an ANTLR IFullIdContext parse-tree node to a QualifiedName. Each Uid segment is read via GetText() (which preserves source casing), then normalized per caseSensitive.
The typical call site is table-name resolution:
tbl := semantic.FromFullIdContext(tblCtx.FullId(), false)
if resolved, err := catalog.LookupTable(tbl); err != nil { ... }
Returns the zero QualifiedName when ctx is nil or has no Uid children — callers should test IsZero before trusting the result.
func FromSegments ¶
func FromSegments(segments []string, caseSensitive bool) QualifiedName
FromSegments builds a QualifiedName from already-normalized segments (each passed through NormalizeString or equivalent). Empty input returns the zero value.
func ParseQualifiedName ¶
func ParseQualifiedName(raw string, caseSensitive bool) QualifiedName
ParseQualifiedName splits a raw dotted string into a QualifiedName. Each segment is normalized independently (stripped of its own surrounding quotes, case-folded unless caseSensitive or quoted).
Semantics match Java's token-per-segment handling: `t."X"` parses as qualifier=[T], name="X" (first segment upper-cased because unquoted, second preserved because quoted).
Quote-embedded dots are NOT handled (e.g. `"a.b".c` still splits on every dot). The ANTLR parser already tokenises individual identifiers, so callers feeding us pre-tokenised segments won't hit this edge — see FromSegments for that path.
func (QualifiedName) EqualsIgnoreQuoting ¶
func (q QualifiedName) EqualsIgnoreQuoting(other QualifiedName) bool
EqualsIgnoreQuoting compares segment-by-segment by normalized text only, ignoring per-segment quoting flags. This is the common "these target the same database object" semantics that Java's Identifier.equals uses.
func (QualifiedName) IsQualified ¶
func (q QualifiedName) IsQualified() bool
IsQualified reports whether the name has at least one qualifier segment preceding the leaf.
func (QualifiedName) IsZero ¶
func (q QualifiedName) IsZero() bool
IsZero reports whether q is the zero-value (empty) QualifiedName.
func (QualifiedName) LeafIdentifier ¶
func (q QualifiedName) LeafIdentifier() Identifier
LeafIdentifier returns the leaf segment wrapped as an Identifier (preserving the leaf's wasQuoted flag). Useful when a caller resolved a qualified name and now wants to work with just the column name.
func (QualifiedName) Name ¶
func (q QualifiedName) Name() string
Name returns the leaf (last) segment. For `schema.table.col` this is `col`. Zero QualifiedName returns the empty string.
func (QualifiedName) PrefixedWith ¶
func (q QualifiedName) PrefixedWith(prefix QualifiedName) bool
PrefixedWith reports whether q starts with prefix's segments. `schema.table.col` is prefixed with `schema.table`. Useful for resolving a bare column against a set of alias-qualified candidates.
func (QualifiedName) Qualifier ¶
func (q QualifiedName) Qualifier() []string
Qualifier returns the segments before the leaf. For `schema.table.col` this returns [schema, table]. An unqualified name returns an empty slice (never nil).
func (QualifiedName) Segments ¶
func (q QualifiedName) Segments() []string
Segments returns all segments, leaf-last. Zero QualifiedName returns nil; otherwise a defensive copy.
func (QualifiedName) String ¶
func (q QualifiedName) String() string
String returns the canonical dotted representation. Suitable for map keys (two QualifiedNames with the same String value are equal under EqualsIgnoreQuoting).
type Scope ¶
type Scope struct {
// contains filtered or unexported fields
}
Scope is the set of named resolutions visible at a point during query analysis. A scope knows about the FROM-clause sources (tables + their aliases) at that level and, via parent-chain, inherits correlated sources from enclosing scopes (for nested subqueries).
Mirrors the subset of Java's `LogicalPlanFragment` + scope chain the analyzer uses for identifier resolution.
Construction: start with NewScope(parent) — parent nil means the outermost query. Call AddSource to push each FROM source as the analyzer walks FROM clauses left-to-right.
Not concurrency-safe; the analyzer is single-threaded per query.
func NewScope ¶
NewScope constructs a Scope inheriting from parent. parent may be nil for the outermost query.
func (*Scope) AddSource ¶
func (s *Scope) AddSource(src ScopeSource) error
AddSource appends a FROM-clause source. Returns an error on duplicate alias within the same scope (SQL forbids two sources sharing an alias at the same level).
func (*Scope) AllSourcesRecursive ¶
func (s *Scope) AllSourcesRecursive() []ScopeSource
AllSourcesRecursive returns sources from this scope and every ancestor, inner-first. Useful for "did you mean?" error suggestions when a qualifier misses — callers can enumerate all visible aliases and suggest the closest.
func (*Scope) ResolveColumn ¶
func (s *Scope) ResolveColumn(id Identifier) (Column, ScopeSource, error)
ResolveColumn looks up a bare column reference (no qualifier) against the scope's sources, following the parent chain if no local match. Ambiguous matches within a single scope level (multiple tables with a column of this name) return an error — the caller should instruct the user to qualify.
Mirrors Java's resolution: inner scopes shadow outer; within a scope, ambiguity is a hard error.
func (*Scope) ResolveQualifiedColumn ¶
func (s *Scope) ResolveQualifiedColumn(qualifier, col Identifier) (Column, ScopeSource, error)
ResolveQualifiedColumn handles `alias.col` — looks up a source whose Alias matches the qualifier, then the column on that source's Table. Unlike ResolveColumn, a qualifier-matched source is unambiguous; if the qualifier doesn't match any source in any enclosing scope, returns SourceNotFoundError.
func (*Scope) Sources ¶
func (s *Scope) Sources() []ScopeSource
Sources returns the FROM-clause sources at this scope level (defensive copy, does NOT include parent sources).
type ScopeSource ¶
type ScopeSource struct {
// Table is the resolved schema-level table.
Table Table
// Alias is the name used to reference this source in the
// enclosing query (column qualifier). For `FROM t AS x` → Alias
// is `x`; for `FROM t` with no alias → Alias is `t`.
Alias Identifier
// CorrelationName is the identifier the analyzer uses to tie
// this source back to a Quantifier when building
// QuantifiedObjectValue / FieldValue trees that reference it.
// Stored as a string so the semantic package doesn't take a
// dependency on cascades/values/CorrelationIdentifier — callers wrap
// this into a cascades.values.CorrelationIdentifier themselves.
CorrelationName string
// Shadowing marks a source whose columns SHADOW same-named columns of
// non-shadowing sources at this scope level (instead of colliding into
// an ambiguity error). A lateral array unnest (`FROM t, t.arr AS x`)
// uses this: its AS/AT binding shadows a same-named real column of `t`
// — Java's generateCorrelatedFieldAccess binding wins over the outer
// (RFC-142). When ≥1 shadowing source matches a bare column, the
// shadowing match is taken and the non-shadowing matches are ignored;
// two shadowing matches are still ambiguous.
Shadowing bool
}
ScopeSource is one FROM-clause entry: a resolved Table plus the alias it's visible under. Alias is always non-zero — when the user doesn't write AS, the Table's own name fills in.
type SourceNotFoundError ¶
type SourceNotFoundError struct {
Alias Identifier
Available []Identifier
}
SourceNotFoundError is returned when a qualifier doesn't match any FROM-clause alias in the scope chain. Carries the list of available aliases (inner-first) so callers can render a "did you mean?" suggestion.
func (*SourceNotFoundError) Error ¶
func (e *SourceNotFoundError) Error() string
type StaticTable ¶
type StaticTable struct {
TableName QualifiedName
TableColumns []Column
TableIndexes []string
}
StaticTable is a test-friendly Table impl backing InMemoryCatalog. Production code should implement Table directly (bridging to RecordType) rather than use this value-type.
func (*StaticTable) Columns ¶
func (t *StaticTable) Columns() []Column
Columns implements Table; returns a defensive copy so callers can't mutate the backing slice.
func (*StaticTable) LookupColumn ¶
func (t *StaticTable) LookupColumn(id Identifier) (Column, bool)
LookupColumn implements Table — case-insensitive match on Identifier.Name.
type Table ¶
type Table interface {
// Name returns the qualified table name.
Name() QualifiedName
// Columns returns the table's column definitions in declared
// order. Empty slice if the table has no columns (a valid state
// for views / CTEs). Never nil.
Columns() []Column
// LookupColumn returns a Column by identifier, matching
// case-insensitively under SQL rules (the Identifier's
// normalized form). Returns (Column{}, false) if no match.
LookupColumn(id Identifier) (Column, bool)
// Indexes returns the index names defined on this table. The
// seed returns just names; richer IndexInfo follows once
// index-pushdown rules need per-index metadata.
Indexes() []string
}
Table is the analyzer's view of a single SQL table. Minimal for the seed — Name + Columns + Indexes. Richer methods (PK shape, fields-of-interest, RecordType bridging) land as the analyzer grows.
type TableNotFoundError ¶
type TableNotFoundError struct {
Name QualifiedName
}
TableNotFoundError is returned when ResolveTable can't find a table. Carries the qualified name the caller requested; follows the error-type pattern from CLAUDE.md (Java exception = Go error struct).
func (*TableNotFoundError) Error ¶
func (e *TableNotFoundError) Error() string
type UnsupportedFromShapeError ¶
type UnsupportedFromShapeError struct {
Shape string
}
UnsupportedFromShapeError signals a FROM-clause shape the seed analyzer doesn't handle yet. Carried up so callers can fall back to the existing logical-builder path rather than erroring out at the SQL level.
func (*UnsupportedFromShapeError) Error ¶
func (e *UnsupportedFromShapeError) Error() string