Documentation
¶
Overview ¶
Package logical holds the Phase 3 (TODO.md §"Phase 3 — Semantic analysis") logical-operator hierarchy. A LogicalOperator describes WHAT a SQL query is doing — scan this table, filter on that predicate, project these columns, sort by those columns — without committing to HOW. Semantic analysis (porting of Java's `SemanticAnalyzer`) produces a LogicalOperator tree from the parse tree. Phase 4 Cascades consumes the tree and emits physical plans; until Cascades lands, the naive Generator can consume LogicalOperator directly through a thin translator.
Naming mirrors Java's `fdb-relational-core/recordlayer/query/LogicalOperator.java` + siblings, trimmed to the core operator set we actually need for the current yamsql corpus:
- LogicalScan — `FROM tbl` (single-table read)
- LogicalFilter — `WHERE pred`
- LogicalProject — `SELECT a, b, expr AS n`
- LogicalSort — `ORDER BY …`
- LogicalLimit — `LIMIT n OFFSET m`
- LogicalAggregate — `GROUP BY … + agg(…)`
- LogicalJoin — `INNER / LEFT / RIGHT JOIN`
- LogicalUnion — `UNION [ALL]`
- LogicalInsert — `INSERT INTO tbl VALUES / INSERT SELECT`
- LogicalUpdate — `UPDATE tbl SET col = expr WHERE …`
- LogicalDelete — `DELETE FROM tbl WHERE …`
- LogicalDDL — `CREATE / DROP` passthrough (no tree shape)
- LogicalCTE — `WITH name AS (…) SELECT …` (non-recursive + recursive)
**Phase 3 scope:** this package is the TARGET SHAPE only. The semantic analyzer that translates parse tree → LogicalOperator and the translator that turns LogicalOperator → executable Plan are separate Phase 3 deliverables. Committing to the shape here lets them land incrementally without churning all of the pkg/ relational tree at once.
**Java alignment.** Most operators map 1:1 to a Java counterpart:
LogicalFilter ↔ LogicalFilter / QueryPredicate-carrying child LogicalProject ↔ the query block's SelectExpression result value LogicalSort ↔ LogicalSortExpression LogicalAggregate ↔ GroupByExpression LogicalUnion ↔ LogicalUnionExpression LogicalInsert ↔ InsertExpression LogicalUpdate ↔ UpdateExpression LogicalDelete ↔ DeleteExpression
Two deliberate divergences:
- `LogicalScan` does not exist in Java-Cascades; Java represents a FROM-source as a Cascades `FullUnorderedScanExpression` wrapped by a `Quantifier`. RFC-022 argues that Phase 3 should own a pure logical-plan representation distinct from Cascades; LogicalScan is the scan-stand-in at the logical level.
- `LogicalJoin` does not exist in Java-Cascades as a discrete type; Java encodes joins via a `SelectExpression` binding multiple `Quantifier`s. Same rationale: keeping join as an explicit logical operator separates Phase 3 tree-building from Phase 4 Cascades translation.
Both divergences are documented in RFC-023 / TODO Phase 3+4.
Explicit predicate / expression representation is deferred. For now LogicalFilter / LogicalProject etc. carry parse-tree handles (antlr IExpressionContext). RFC-021 Phase 2 replaces those with `Value` nodes from the Cascades Value hierarchy. RFC-023 committed to non-generic interfaces + `any`; the seed lives in `pkg/recordlayer/query/plan/cascades/`. As that package grows this one will migrate its text-handle predicates to real Values.
Index ¶
- func BindCTESources(op LogicalOperator, registry CTERegistry)
- func CorrelatedOnlyTo(v values.Value, set map[values.CorrelationIdentifier]struct{}) bool
- func FindOuterScanTable(op LogicalOperator, alias string) string
- func IsUnnestOrdinalAlias(op LogicalOperator, alias string) bool
- func OuterSourceIsDerivedTable(op LogicalOperator, alias string) bool
- func ReadsBuildingCTE(op LogicalOperator) bool
- func ReferencesCTE(op LogicalOperator, producer *CTEProducer) bool
- func ReferencesCTEInScope(op LogicalOperator, producer *CTEProducer, registry CTERegistry) bool
- func UnnestBindingName(binding, alias, atAlias string) string
- func UnnestOrdinalityNames(alias, atAlias string) []string
- type AggCallProvenance
- type AggregateCall
- type AggregateOutputSlot
- type Assignment
- type BoundJoinPredicate
- type CTEIdentity
- type CTEOption
- type CTEProducer
- func (p *CTEProducer) Body() LogicalOperator
- func (p *CTEProducer) BuildingSeed() LogicalOperator
- func (p *CTEProducer) ColumnAliases() []string
- func (p *CTEProducer) DefiningRegistry() CTERegistry
- func (p *CTEProducer) Identity() *CTEIdentity
- func (p *CTEProducer) InsertBinding() values.CorrelationIdentifier
- func (p *CTEProducer) Name() string
- func (p *CTEProducer) NamePath() []string
- func (p *CTEProducer) Recursive() bool
- func (p *CTEProducer) ScanBinding() values.CorrelationIdentifier
- func (p *CTEProducer) SetBuildingSeed(seed LogicalOperator)
- func (p *CTEProducer) TraversalOrder() TraversalOrder
- func (p *CTEProducer) WithBody(body LogicalOperator) *CTEProducer
- type CTERegistry
- type ColumnRef
- type CorrelatedScalarSubquery
- type ExistsConstraint
- type ExistsConsumer
- type ExistsInput
- type ExistsSubquery
- type GroupKey
- type JoinKind
- type LogicalAggregate
- type LogicalCTE
- type LogicalDDL
- type LogicalDelete
- type LogicalDistinct
- type LogicalFilter
- type LogicalInlineValues
- func (*LogicalInlineValues) Children() []LogicalOperator
- func (v *LogicalInlineValues) CollectionValue() values.Value
- func (v *LogicalInlineValues) Explain(indent string) string
- func (v *LogicalInlineValues) ResultType() values.Type
- func (v *LogicalInlineValues) SetStream(stream values.Value)
- func (v *LogicalInlineValues) StreamValue() values.Value
- type LogicalInsert
- type LogicalJoin
- type LogicalLimit
- type LogicalOperator
- type LogicalProject
- type LogicalScan
- type LogicalSingleton
- type LogicalSort
- type LogicalUnion
- type LogicalUnnest
- type LogicalUpdate
- type ScalarSubquery
- type ScanSource
- type SortDir
- type SortKey
- type TraversalOrder
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BindCTESources ¶
func BindCTESources(op LogicalOperator, registry CTERegistry)
BindCTESources seals source ownership during construction, before the graph is shared. Prepared definitions are never revisited under a consumer's registry. Read-side lookup uses ResolveScan and does not publish bindings.
func CorrelatedOnlyTo ¶
func CorrelatedOnlyTo(v values.Value, set map[values.CorrelationIdentifier]struct{}) bool
CorrelatedOnlyTo reports whether v is correlated, and only to aliases in set.
func FindOuterScanTable ¶
func FindOuterScanTable(op LogicalOperator, alias string) string
FindOuterScanTable resolves a lateral unnest's outer source alias to the scanned table name by matching a LogicalScan whose source alias is `alias` (case-insensitive) among the VISIBLE FROM-scope sources of the outer leg `op`. It is used to resolve the outer source of a lateral unnest (`FROM t, t.arr AS x` → the scan of `t`) so its proto descriptor can be inspected for the array field.
The walk MUST NOT descend into a CTE / derived-table BODY. A derived table `(SELECT … FROM T1) AS d` lowers to a `LogicalCTE{Body: <…scan of T1…>, Main: Scan(d)}`; only `d` is a visible source — `T1` is hidden inside the body and out of scope. Descending into `Body` would match the hidden `T1` scan and explode a correlated array against a source the query can't see (a silent-wrong / mis-classification). So at a `LogicalCTE` we resolve ONLY against its `Main` (the visible alias projection), never its `Body`. This mirrors Java's `resolveCorrelatedIdentifier` resolving against `getLogicalOperatorsIncludingOuter()` — the in-scope quantifiers, not nested query bodies.
Returns "" when no VISIBLE matching scan is found (a non-scan outer, or a name hidden behind a derived-table boundary), so the caller falls back to the table path.
func IsUnnestOrdinalAlias ¶
func IsUnnestOrdinalAlias(op LogicalOperator, alias string) bool
IsUnnestOrdinalAlias reports whether `alias` names a prior lateral unnest's AT ORDINAL alias (`t.arr AS x AT o` → `o`) in the outer sub-plan `op`. That alias binds a scalar INTEGER ordinal, so a field access on it (`o.sub`) is a resolution error, not a chainable source — the caller surfaces the honest UNDEFINED_COLUMN Java produces instead of the generic unnest fallback. Same walk as FindOwnerUnnest (no CTE-Body descent), matching the AT alias only.
func OuterSourceIsDerivedTable ¶
func OuterSourceIsDerivedTable(op LogicalOperator, alias string) bool
OuterSourceIsDerivedTable reports whether `alias` (a lateral unnest's segment-0 outer source name) is bound, in the outer sub-plan `op`, to a DERIVED-TABLE / CTE leg — i.e. a `LogicalCTE` whose Name equals `alias`. It reads the logical tree STRUCTURALLY (not a translator-internal cteScope map), so it fires independent of cteScope population order and regardless of whether the alias also names a real same-named base table.
A derived table `(SELECT …) AS D` lowers to a `LogicalCTE{Name:D, Main:Scan(D)}` inside the outer leg. When such a leg shadows a real same-named table, validating the unnest's array field against the base-table descriptor would explode the WRONG column (a derived-output silent-wrong). Detecting the derived/CTE leg here — by the in-scope quantifier alias, exactly as Java's generateCorrelatedFieldAccess resolves the in-scope source rather than the catalog table — lets the caller reject (or skip a base-table check for) the derived-output unnest in ALL cases.
Like FindOuterScanTable, it does NOT descend into a CTE's Body (only its Main): a derived table is its own FROM scope; a same-named CTE nested inside another derived body is out of the current scope.
func ReadsBuildingCTE ¶
func ReadsBuildingCTE(op LogicalOperator) bool
ReadsBuildingCTE reports whether op reads a recursive declaration whose body is still being built: the self-reference of a recursive leg, which only the recursive translation can lower (to its temporary table scan).
func ReferencesCTE ¶
func ReferencesCTE(op LogicalOperator, producer *CTEProducer) bool
ReferencesCTE follows selected identities, including retained dependencies.
func ReferencesCTEInScope ¶
func ReferencesCTEInScope(op LogicalOperator, producer *CTEProducer, registry CTERegistry) bool
ReferencesCTEInScope also resolves unbound constructor inputs in the reader's lexical scope. It never seals scan ownership or prepares declarations.
func UnnestBindingName ¶
UnnestBindingName reads a lateral source's runtime identity. A nonempty binding is the parser's collision-free identity; the empty-binding convention uses the captured AS/default name (AT for programmatic ordinal-only sources). Display aliases never replace a carried binding at a consumer.
func UnnestOrdinalityNames ¶
UnnestOrdinalityNames names the two physical slots without changing their order. Semantic labels remain untouched. For AT-only input the visible ordinal keeps its name; only the hidden element is renamed on a collision.
Types ¶
type AggCallProvenance ¶
type AggCallProvenance struct {
// AggColIdx is the index of the producing column; -1 for the synthesized
// COUNT(*), which has no parsed column and no operand to resolve.
AggColIdx int
// Operand is that column's operand text as rendered BEFORE strip.
Operand string
}
AggCallProvenance records which parsed aggregate column produced one AggregateCall, and what that column's operand rendered to BEFORE the producer's qualifier strip.
The pre-strip text is the load-bearing part. The producer names a call's operand as `strip(arg)` — the strip removes a same-source qualifier, and on the derived-table path it is non-empty — while a consumer holding only `aggCols` can recompute `arg` and has no way to reproduce `strip`. Comparing the stripped text against a recomputed unstripped one rejects valid queries: `SELECT SUM(d.a + d.b) FROM (SELECT a, b FROM sales) d` renders `D.A+D.B`, stores `A+D.B`, and matches neither spelling.
So the comparison happens on the pre-strip form, which both sides can produce from the same renderer. That is not a paired assertion that moves together: it is frozen here at production and recomputed at consumption from the live columns, so a change to the renderer moves both and passes (correctly — a re-rendering does not invalidate the correspondence), while a change to the COLUMNS moves one side only, which is the event being watched.
type AggregateCall ¶
type AggregateCall struct {
Func string // upper-case aggregate function name (COUNT/SUM/MIN/MAX/AVG)
Operand string // canonical operand text; "*" for COUNT(*)
Star bool // COUNT(*) (or COUNT(<non-null const>) collapsed to it)
Distinct bool
// IgnoreNulls and Limit are ARRAY_AGG's options (values.ArrayAggNoLimit
// when uncapped).
IgnoreNulls bool
Limit int
// BareColumn: the operand is a single column reference PER THE PARSE
// TREE — a lazy FieldValue read of Operand is well-defined without
// catalog resolution. False for computed/expression operands, which
// require a resolved Value.
BareColumn bool
// Qualified: a BareColumn operand carried a table qualifier PER THE
// PARSE TREE (FullId segment count > 1) — never a dot scan of the
// rendered name, which a delimited identifier containing a literal dot
// would false-positive. Always false for computed operands; consumers
// gating on qualification fall back to a conservative canonical-text
// scan there (a dot inside a computed rendering only ever comes from a
// real qualified reference or a quoted literal, and the gate is
// optimization-only).
Qualified bool
// Bare/Qualifier are the SEGMENTS behind Operand for a BareColumn — the
// last segment and the leading one(s). Qualified already said whether a
// qualifier exists; these say what it IS, which is what a consumer needs to
// resolve the operand WITHOUT slicing the rendered text apart at its first
// dot. Empty for computed operands and for producers that have not been
// taught to carry them; read them only through Ref().
Bare string
Qualifier string
}
AggregateCall is the STRUCTURED form of one aggregate in the SELECT list, captured from the parse tree at build time — function, operand text (for result-map keying), and the star/distinct/bare-column classification. The translator consumes this instead of re-parsing the display text in Aggregates: a missing entry is a typed decline, never a string split (RFC-180 F-1 — the text reparse mangled nested arithmetic and silently dropped HAVING groups).
func (AggregateCall) CanonicalName ¶
func (c AggregateCall) CanonicalName() string
CanonicalName renders the call as `FUNC(OPERAND)`, `FUNC(DISTINCT OPERAND)` or `COUNT(*)` — the alias-free output-column name for an aggregate. The FUNC half is upper because it is written that way from an enum, never folded from the SQL; the OPERAND half is whatever the producer minted and is not touched here.
Not every consumer folds case: normalizeAggOutputName and normalizeAggregateBindingName strip whitespace only (RFC-237), and several consumers compare exactly. The upper-case Func is safe because it is a literal, not because a fold on the far side rescues it. (Consumers that apply their own strings.ToUpper, like the aggTypes map, fold symmetrically on write and read, so that is a consistent key, not a naming decision.)
func (AggregateCall) Ref ¶
func (c AggregateCall) Ref() ColumnRef
Ref is the operand's segment triple, reconciled against its canonical text. A COUNT(*) or computed operand captures nothing: its Operand is a rendering, and BareColumn is what says so.
type AggregateOutputSlot ¶
AggregateOutputSlot preserves one visible aggregate SELECT item after the parser's internal key/call harvesting and reordering. SelectOrdinal is one-based and unique. NativeOrdinal is zero-based into [group keys..., aggregate calls...], or -1 for a computed expression.
type Assignment ¶
type Assignment struct {
// Column is the SET column's identifier for display, its segments joined
// by '.' as Java prints an Identifier (`Ambiguous reference X.X.F`). It is
// never parsed back: a quoted segment holding a dot would split wrong, so
// every reader of the path reads Segments or the resolved FieldOrdinals.
// Its readers are the refusals' messages and Explain.
Column string
// Segments is the SET column's identifier, one normalized segment per
// part (a quoted part keeps its case and any '.').
Segments []string
Expr string // canonical text
Value values.Value
// FieldOrdinals and FieldNames are the target field the identifier
// resolved to, set by the catalog-aware builder and read by everything
// after it (nil until then): the column's position in the target
// descriptor, then each struct field's position in the struct before it,
// and the fields' names (user identifiers) along the way.
FieldOrdinals []int
FieldNames []string
}
Assignment is one SET clause entry. Expr is the canonical text (used for explain and as a fallback); Value is the resolved RHS expression Value (populated by the catalog-aware builder) that the executor evaluates against each target row. A nil Value means the text builder ran without catalog resolution.
type BoundJoinPredicate ¶
type BoundJoinPredicate struct {
Predicate predicates.QueryPredicate
Exists []ExistsSubquery
VisibleBindings []values.CorrelationIdentifier
}
BoundJoinPredicate retains ON provenance before existential folding. The source identities describe the left-to-current frame, never later joins.
type CTEIdentity ¶
type CTEIdentity struct {
// contains filtered or unexported fields
}
CTEIdentity identifies a declaration across immutable lowering snapshots.
type CTEOption ¶
type CTEOption struct {
// contains filtered or unexported fields
}
func CTEColumns ¶
func CTENamePath ¶
CTENamePath captures the declaration's normalized name and qualifier without flattening quoted dots. Synthetic declarations default to one literal name.
func CTETraversal ¶
func CTETraversal(order TraversalOrder) CTEOption
type CTEProducer ¶
type CTEProducer struct {
// contains filtered or unexported fields
}
CTEProducer owns one declaration. After preparation its definition and defining registry are immutable; envelopes and scans share this same record. Consumer correlation identifiers belong to scans, never to this record.
func PrepareCTE ¶
func PrepareCTE(name string, recursive bool, defining CTERegistry, build func(CTERegistry) (LogicalOperator, error), options ...CTEOption, ) (*CTEProducer, error)
PrepareCTE builds a declaration once, before publishing it. Recursive bodies receive a private self binding during construction, matching Java's temporary scan registration. A failed build never escapes into the caller's registry.
func ResolveScan ¶
func ResolveScan(scan *LogicalScan, registry CTERegistry) *CTEProducer
ResolveScan is a read-only source lookup. Captured physical ownership cannot fall through to a later, same-named declaration. Unbound constructor inputs use this caller's registry without publishing it into a shared expression; only construction's BindCTESources seals that choice.
func (*CTEProducer) Body ¶
func (p *CTEProducer) Body() LogicalOperator
func (*CTEProducer) BuildingSeed ¶
func (p *CTEProducer) BuildingSeed() LogicalOperator
BuildingSeed is the seed leg of a recursive declaration whose body is still being built, else nil.
func (*CTEProducer) ColumnAliases ¶
func (p *CTEProducer) ColumnAliases() []string
func (*CTEProducer) DefiningRegistry ¶
func (p *CTEProducer) DefiningRegistry() CTERegistry
func (*CTEProducer) Identity ¶
func (p *CTEProducer) Identity() *CTEIdentity
func (*CTEProducer) InsertBinding ¶
func (p *CTEProducer) InsertBinding() values.CorrelationIdentifier
func (*CTEProducer) Name ¶
func (p *CTEProducer) Name() string
func (*CTEProducer) NamePath ¶
func (p *CTEProducer) NamePath() []string
func (*CTEProducer) Recursive ¶
func (p *CTEProducer) Recursive() bool
func (*CTEProducer) ScanBinding ¶
func (p *CTEProducer) ScanBinding() values.CorrelationIdentifier
func (*CTEProducer) SetBuildingSeed ¶
func (p *CTEProducer) SetBuildingSeed(seed LogicalOperator)
SetBuildingSeed records a recursive declaration's seed leg for references met while its body is being built.
func (*CTEProducer) TraversalOrder ¶
func (p *CTEProducer) TraversalOrder() TraversalOrder
func (*CTEProducer) WithBody ¶
func (p *CTEProducer) WithBody(body LogicalOperator) *CTEProducer
WithBody creates an immutable lowering snapshot. Its declaration identity, aliases, recursive bindings and captured registry survive the body rewrite.
type CTERegistry ¶
type CTERegistry struct {
// contains filtered or unexported fields
}
CTERegistry is a persistent lexical registry. Extending it never changes the environment retained by an earlier declaration, including names absent there.
func (CTERegistry) BodyScope ¶
func (r CTERegistry) BodyScope(producer *CTEProducer) CTERegistry
BodyScope selects a producer's defining frame without publishing it. Prepared declarations retain their frame; constructor inputs are interpreted in the lexical frame that introduced them in this reader's persistent registry.
func (CTERegistry) Lookup ¶
func (r CTERegistry) Lookup(name string, path ...string) *CTEProducer
Lookup compares captured, normalized identifier segments when supplied. Only legacy string-only callers use a flattened, case-insensitive name.
func (CTERegistry) Names ¶
func (r CTERegistry) Names() []string
func (CTERegistry) With ¶
func (r CTERegistry) With(p *CTEProducer) CTERegistry
type ColumnRef ¶
type ColumnRef struct {
Present bool
Bare string // last segment
Qualifier string // leading segment(s); "" when unqualified
Qualified bool // parse-tree segment count > 1
}
ColumnRef is the parse-tree segment triple of a plain column reference. Present mirrors SortKey/GroupKey's convention of a populated Bare, made explicit because an unqualified reference and an uncaptured one are otherwise the same zero value and mean opposite things.
func ColumnRefFor ¶
ColumnRefFor states a segment triple against the name that was actually RENDERED for it, and is the only way a ColumnRef should be built from a producer's fields.
The reconciliation is the invariant, not a formality. The rendered name is not always the segments joined: the derived-table shell strips a `X.` qualifier prefix off the rendering, and a rebase can rewrite a key's text entirely. A triple describing a DIFFERENT string than the one downstream carries is worse than no triple, because it is trusted — told a bare `ID` is qualified by X, a consumer looks for leg X's ID instead of the flat column the shell just produced.
So Present is claimed only when the triple demonstrably spells the rendered name. Anything else reads as "not captured", which every consumer already handles by keeping the behaviour it had.
It lives here rather than beside any one producer because three of them now build these — projected columns, ORDER BY keys and aggregate operands — and three copies of an invariant is three chances for one to drift.
type CorrelatedScalarSubquery ¶
type CorrelatedScalarSubquery struct {
// rows, so SQL standard at-most-one-row semantics apply: a second inner row
// per outer row is a cardinality violation (21000), not silent truncation.
// It is false when exact pagination caps a multi-row-capable source to 0/1,
// or when the source is intrinsically <=1 (for example a non-grouped real
// aggregate, for which even LIMIT >1 remains safe). Data-dependent LIMIT >1
// is rejected before this IR until post-pagination scalar collapse exists.
StrictSingle bool
}
CorrelatedScalarSubquery pairs a correlation alias with a logical plan for a correlated scalar subquery like `(SELECT COUNT(*) FROM orders o WHERE o.customer_id = c.id)`. The inner plan has the correlation predicate baked in as a filter child of the aggregate — the executor re-evaluates it per outer row via FlatMap. Carried on LogicalProject or LogicalFilter, depending on whether the scalar is consumed by SELECT or WHERE.
type ExistsConstraint ¶
type ExistsConstraint uint8
ExistsConstraint is a requirement derived from a bound child. It is not a correlation property: the same child has the same requirement in every clause.
const ( ExistsAnyConsumer ExistsConstraint = iota ExistsPositivePredicateOnly )
type ExistsConsumer ¶
type ExistsConsumer uint8
ExistsConsumer describes a resolved use, not an assumed context at the child callback. Zero is deliberately not a positive-consumer default.
const ( ExistsPositivePredicate ExistsConsumer = 1 << iota ExistsNegativePredicate ExistsProjectedValue ExistsBooleanPredicate )
type ExistsInput ¶
type ExistsInput struct {
// contains filtered or unexported fields
}
ExistsInput owns the lowered child of an existential edge. Its exact row is captured from the producer, not reconstructed from SQL column labels. The enclosing translator consumes this same Reference and the child's independent scalar registrations. Copies of the attachment share the immutable input.
Mirrors Java ExpressionVisitor.visitExistsExpressionAtom: the existential quantifier ranges over the query operator's already-constructed Reference. Consumer admission is a separate obligation of the owning clause.
func NewDeferredExistsInput ¶
func NewDeferredExistsInput(row values.Type, scalars []ScalarSubquery) (*ExistsInput, error)
NewDeferredExistsInput is the input of an EXISTS over a recursive declaration still being built: typed now, by the declaration's seed, and lowered by the translator inside the recursive translation, where the self-reference is the temporary table scan. Its Reference is nil.
func NewExistsInput ¶
func NewExistsInput(reference *expressions.Reference, scalars []ScalarSubquery) (*ExistsInput, error)
func (*ExistsInput) Reference ¶
func (i *ExistsInput) Reference() *expressions.Reference
Reference is the lowered producer, nil for a deferred input.
func (*ExistsInput) ResultType ¶
func (i *ExistsInput) ResultType() values.Type
func (*ExistsInput) Scalars ¶
func (i *ExistsInput) Scalars() []ScalarSubquery
type ExistsSubquery ¶
type ExistsSubquery struct {
Alias values.CorrelationIdentifier
// FlowedType is the exact whole-row type of Plan. It is captured when the
// subquery is built so every ExistsValue and existential Quantifier use one
// stable type authority rather than re-deriving a placeholder type.
FlowedType values.Type
// Input owns the lowered producer and its scalar edges. Plan retains the
// bound logical source/placement information; it is not rebuilt to type or
// translate an attachment that already owns Input.
Input *ExistsInput
Plan LogicalOperator
JoinPredicate predicates.QueryPredicate
// KnownTruth is non-nil when the front-end can prove the EXISTS result from
// relational cardinality alone. A non-grouped aggregate produces exactly one
// row before pagination; applying a literal LIMIT/OFFSET therefore makes the
// result statically TRUE (the row survives) or FALSE (the row is skipped).
// LIMIT 0 is likewise empty for every supported inner shape. The translator
// substitutes the constant in positive and negated WHERE consumers instead
// of building a correlated semi-join. Nil means the result is data-dependent.
KnownTruth predicates.TriBool
// Constraint is an admission requirement, independent of dependency and
// predicate placement. Only the completed owning clause can discharge it.
Constraint ExistsConstraint
// contains filtered or unexported fields
}
ExistsSubquery pairs an existential alias with the logical plan for an EXISTS subquery. Carried on LogicalFilter so the Cascades translator can build ExistentialQuantifiers over the subquery plans.
func (ExistsSubquery) AdmitConsumer ¶
func (e ExistsSubquery) AdmitConsumer(use ExistsConsumer) (ExistsSubquery, error)
AdmitConsumer returns a copy carrying a proof for this use. Every use must satisfy the edge's constraint before its owner publishes the attachment.
func (ExistsSubquery) ValidateAdmission ¶
func (e ExistsSubquery) ValidateAdmission() error
ValidateAdmission checks the construction proof without reclassifying Values in the translator. Programmatic constrained edges must also call AdmitConsumer with an explicit resolved context; omitting it cannot bypass admission.
type GroupKey ¶
type GroupKey struct {
Display string
Bare string // last segment of a bare column ref; "" for expression keys
Qualifier string // leading segment(s) when qualified; "" otherwise
Qualified bool // parse-tree segment count > 1
// Segs is the FULL ordered segment list of a bare column reference
// (`r.v.z` -> [R V Z]); nil for expression keys. Qualifier joins the
// leading segments into one string and so cannot say where one segment
// ends and the next begins — `R.V` reads as a source alias, which is why
// a grouping key that descends INTO a struct column could only be
// resolved once the segments travelled with it. Resolution consumes Segs;
// Qualifier/Display remain renderings.
Segs []string
Value values.Value
}
GroupKey is one GROUP BY key in structured form (RFC-180 F-3): the display text is a rendering for output naming and diagnostics, never re-parsed; qualification and segments are parse-tree truth; Value is the resolved key (expressions always resolve; a bare column's Value is filled by the upgrade passes, nil = lazy bare read).
type LogicalAggregate ¶
type LogicalAggregate struct {
Input LogicalOperator
GroupKeys []GroupKey
// Calls is the SOLE aggregate-call representation. Builders populate it
// from the parse tree; the translator requires it (RFC-180 F-3 retired
// the parallel display-text slice).
Calls []AggregateCall
Aliases []string // parallel to Calls
AggregateOperands []values.Value // resolved operand Values (parallel to Calls); nil slot = use text
HasDistinctAggregate bool // true when any aggregate uses DISTINCT (e.g. COUNT(DISTINCT x))
// HasHaving: the query carried a HAVING clause. Presence SENTINEL only
// (RFC-180 F-3 — the former canonical-text field was never re-parsed):
// the translator declines when HasHaving is set but HavingPredicate is
// nil, so an unstructured HAVING can never be silently dropped.
HasHaving bool
HavingPredicate predicates.QueryPredicate
HavingExistsSubqueries []ExistsSubquery // EXISTS subquery plans inside HAVING
HavingScalarSubqueries []ScalarSubquery // scalar subquery plans inside HAVING
// OutputSlots is the visible SQL SELECT contract in SELECT-list order.
// NativeOrdinal addresses the aggregate's physical output row
// [GroupKeys..., Calls...]; -1 marks a computed post-aggregate item.
// The aggregate keeps producing its native row until the legal projection
// boundary above ORDER BY, where this contract is materialized. Public
// labels live exclusively on that Project and are not producer identity.
OutputSlots []AggregateOutputSlot
// CallProvenance maps each Calls entry to the parsed aggregate column that
// produced it, and CallProvenanceCols records how many such columns the
// producer walked. HasCallProvenance says whether a producer recorded any —
// distinct from the slice being nil, which is also what CONSUMED looks like.
//
// It exists because the operand-resolution pass used to RECONSTRUCT this
// correspondence by case-folding the operand's rendered text. That text
// carries identifiers AND string literals, and a fold cannot tell them
// apart, so two aggregates differing only in a literal's case matched each
// other and the second write clobbered the first — a silent wrong answer
// (RFC-241). The producer already knew the correspondence; this carries it
// instead of having it guessed.
//
// PARSER PROVENANCE, NOT PLAN STRUCTURE. Two obligations follow, and both
// are load-bearing rather than tidiness:
// - the operand-resolution pass NILS both on every exit, so nothing
// downstream can read parse-time positions off a logical node;
// - neither may reach memo identity, or two structurally identical
// GroupByExpressions differing only in provenance would land in
// different groups. That holds by construction today: this type never
// enters the cascades package, it is translated into
// expressions.GroupByExpression first.
CallProvenance []AggCallProvenance
CallProvenanceCols int
HasCallProvenance bool
// OuterCorrelations is Java's outerCorrelations as
// LogicalOperator.generateGroupBy receives them: the runtime correlations
// of the enclosing query blocks. They are constant for every row the
// aggregate consumes, so a post-aggregate value correlated only to them is
// composable from the aggregate output (SemanticAnalyzer.isComposableFrom's
// constantCorrelations arm) and pulls up unchanged (Expressions.pullUp's
// constantAliases). Nil for a block with no enclosing sources.
OuterCorrelations map[values.CorrelationIdentifier]struct{}
}
LogicalAggregate runs GROUP BY + aggregate functions on its child. GroupKeys are the grouping-column expressions; Calls holds the structured aggregate calls (see AggregateCall) with parallel Aliases.
func NewAggregate ¶
func NewAggregate(input LogicalOperator, groupKeys []GroupKey, calls []AggregateCall, aliases []string, hasHaving bool) *LogicalAggregate
func (*LogicalAggregate) CallsFromAggCol ¶
func (a *LogicalAggregate) CallsFromAggCol(aggColIdx int) []int
CallsFromAggCol returns the indices of the Calls entries produced by the parsed aggregate column at aggColIdx. Usually one; more than one only when the same column feeds several calls. Empty when the provenance table is absent, which is a producer that resolved its own operands.
Callers must have validated the table first — this trusts it.
func (*LogicalAggregate) Children ¶
func (a *LogicalAggregate) Children() []LogicalOperator
func (*LogicalAggregate) CorrelatedOnlyToOuter ¶
func (a *LogicalAggregate) CorrelatedOnlyToOuter(v values.Value) bool
CorrelatedOnlyToOuter reports whether v is correlated, and only to OuterCorrelations: a value every aggregated row sees as the same constant, e.g. the outer `w.f` in `(SELECT COUNT(*) + w.f FROM h)`.
func (*LogicalAggregate) Explain ¶
func (a *LogicalAggregate) Explain(indent string) string
type LogicalCTE ¶
type LogicalCTE struct {
*CTEProducer
// Alias retains a derived source's SQL qualifier independently of its
// private CTE registration Name (empty = Name is also the qualifier).
Alias string
Main LogicalOperator
// PreserveMainSource marks a scope-only envelope: Name registers Body for
// scans in Main, but the envelope does not replace Main's outward source
// identity. Derived-table alias carriers leave it false because their
// binding is the outward source identity.
PreserveMainSource bool
// Binding is the derived/CTE leg's binding correlation name when its
// FROM alias duplicates an earlier leg's ("" = Name binds). See
// LogicalScan.Binding.
Binding string
}
LogicalCTE retains a Common Table Expression declaration around a Main query. Its consumers select the shared producer through LogicalScan.Source.
func NewCTE ¶
func NewCTE(name string, body, main LogicalOperator, recursive bool, options ...CTEOption) *LogicalCTE
NewCTE constructs a LogicalCTE.
func NewCTEReference ¶
func NewCTEReference(producer *CTEProducer, main LogicalOperator) *LogicalCTE
NewCTEReference retains a prepared declaration without copying its metadata.
func (*LogicalCTE) Children ¶
func (c *LogicalCTE) Children() []LogicalOperator
func (*LogicalCTE) Explain ¶
func (c *LogicalCTE) Explain(indent string) string
type LogicalDDL ¶
LogicalDDL wraps a DDL statement that has no meaningful tree shape (CREATE TABLE, DROP INDEX, …). Kind carries the DDL command ("CREATE TABLE" etc.) and Text the canonical source.
func NewDDL ¶
func NewDDL(kind, text string) *LogicalDDL
func (*LogicalDDL) Children ¶
func (*LogicalDDL) Children() []LogicalOperator
func (*LogicalDDL) Explain ¶
func (d *LogicalDDL) Explain(indent string) string
type LogicalDelete ¶
type LogicalDelete struct {
Target string
// TargetPath is the target's identifier segments (LogicalInsert.TablePath).
TargetPath []string
Input LogicalOperator
}
LogicalDelete removes rows matching Input from Target.
func NewDelete ¶
func NewDelete(target string, input LogicalOperator) *LogicalDelete
func (*LogicalDelete) Children ¶
func (d *LogicalDelete) Children() []LogicalOperator
func (*LogicalDelete) Explain ¶
func (d *LogicalDelete) Explain(indent string) string
type LogicalDistinct ¶
type LogicalDistinct struct {
Input LogicalOperator
}
LogicalDistinct removes duplicate rows from its input.
func NewDistinct ¶
func NewDistinct(input LogicalOperator) *LogicalDistinct
func (*LogicalDistinct) Children ¶
func (d *LogicalDistinct) Children() []LogicalOperator
func (*LogicalDistinct) Explain ¶
func (d *LogicalDistinct) Explain(indent string) string
type LogicalFilter ¶
type LogicalFilter struct {
// HasQualify preserves the owning block's clause provenance when QUALIFY
// is combined with WHERE. Correlated EXISTS admission must not infer that
// provenance from a predicate that may already have folded to a constant.
HasQualify bool
Input LogicalOperator
Predicate predicates.QueryPredicate // preferred when non-nil
PredicateText string // source-text fallback
ExistsSubqueries []ExistsSubquery // subquery plans for EXISTS predicates
ScalarSubqueries []ScalarSubquery // uncorrelated scalar plans (pre-evaluated)
}
LogicalFilter applies a WHERE/HAVING predicate to its child.
Predicate is the preferred representation — a cascades QueryPredicate tree produced by the expr walker. When non-nil, Explain renders it via Predicate.Explain(), which yields the normalised form after simplification (tautology-folded, NOTs pushed to leaves, operands tree-walked).
PredicateText is the fallback: the canonical source text of the WHERE expression. Used when the expression shape is out of the walker's scope (UnsupportedExpressionShapeError) or when the builder is constructed without a metadata-backed catalog (the catalog-less Explain path, which has no transaction in scope).
func NewFilter ¶
func NewFilter(input LogicalOperator, pred string) *LogicalFilter
NewFilter constructs a text-only LogicalFilter — used by the non-catalog-aware logical-builder path where only canonical source text is available. Pair with NewFilterWithPredicate when a predicates.QueryPredicate tree is in scope (catalog-aware builder); the predicate-tree form takes precedence in Explain output when both are set.
func NewFilterWithPredicate ¶
func NewFilterWithPredicate(input LogicalOperator, pred predicates.QueryPredicate, text string) *LogicalFilter
NewFilterWithPredicate constructs a LogicalFilter whose predicate is a cascades QueryPredicate tree. The text form is retained for diagnostics so Explain output stays stable even when the Predicate render differs from the source text (e.g. after tautology-folding).
func (*LogicalFilter) Children ¶
func (f *LogicalFilter) Children() []LogicalOperator
func (*LogicalFilter) Explain ¶
func (f *LogicalFilter) Explain(indent string) string
type LogicalInlineValues ¶
type LogicalInlineValues struct {
Alias string
Binding string
// contains filtered or unexported fields
}
LogicalInlineValues is a multi-row VALUES table source in a FROM clause. Its collection is the exact literal array Java lowers directly to an ExplodeExpression: every array element is one named record row.
This is deliberately distinct from both LogicalSingleton (the single-row, zero-column SELECT-without-FROM source) and LogicalUnnest (a lateral correlated array access). Conflating either shape with an inline table would give it the wrong cardinality or make it participate in lateral-unnest gather/collision rules.
func FindOwnerInlineValues ¶
func FindOwnerInlineValues(op LogicalOperator, alias string) *LogicalInlineValues
FindOwnerInlineValues resolves one visible inline VALUES source in the current logical FROM scope. CTE bodies are separate scopes, so a CTE exposes only its Main here. Duplicate aliases are ambiguous and deliberately return nil instead of selecting whichever leaf the traversal happens to visit first.
func NewInlineValues ¶
func NewInlineValues(alias string, collection values.Value) (*LogicalInlineValues, error)
NewInlineValues constructs an exact literal-table source. alias is the source's query-block correlation (an authored inline-table alias, or a parser-minted private alias when SQL omitted one).
func NewTableFunctionSource ¶
func NewTableFunctionSource(alias string, stream values.Value) (*LogicalInlineValues, error)
NewTableFunctionSource constructs a table-function source over a streaming Value whose Type is the per-row record type.
func (*LogicalInlineValues) Children ¶
func (*LogicalInlineValues) Children() []LogicalOperator
func (*LogicalInlineValues) CollectionValue ¶
func (v *LogicalInlineValues) CollectionValue() values.Value
func (*LogicalInlineValues) Explain ¶
func (v *LogicalInlineValues) Explain(indent string) string
func (*LogicalInlineValues) ResultType ¶
func (v *LogicalInlineValues) ResultType() values.Type
func (*LogicalInlineValues) SetStream ¶
func (v *LogicalInlineValues) SetStream(stream values.Value)
SetStream binds a table function's arguments once its lateral scope is known; the row type is fixed by the function, not by its arguments.
func (*LogicalInlineValues) StreamValue ¶
func (v *LogicalInlineValues) StreamValue() values.Value
type LogicalInsert ¶
type LogicalInsert struct {
Table string
// TablePath preserves the target's normalized identifier segments from
// the SQL parse, as LogicalScan.TablePath does: a quoted dotted name is
// one segment, not a template qualifier. Nil denotes legacy programmatic
// input, resolved from Table by splitting.
TablePath []string
Columns []string
Source LogicalOperator
ValuesArray values.Value
}
LogicalInsert describes an INSERT into Table. Source is the row- producing child (a SELECT); Columns is the projected-column list (may be empty to mean "all columns").
ValuesArray holds the literal VALUES rows as a Cascades array Value (an ArrayConstructorValue of one RecordConstructorValue per row), mutually exclusive with Source. The translator wraps it in an ExplodeExpression so INSERT … VALUES streams through the same Cascades path as INSERT … SELECT — matching Java's RecordConstructorValue → array → Explode → Insert shape. It is a typed Value rather than a child operator because the rows are built from evaluated literals (parameters are already substituted at plan time), which needs the connection's evaluation context the pure logical builder lacks.
func NewInsert ¶
func NewInsert(table string, cols []string, source LogicalOperator) *LogicalInsert
func (*LogicalInsert) Children ¶
func (i *LogicalInsert) Children() []LogicalOperator
func (*LogicalInsert) Explain ¶
func (i *LogicalInsert) Explain(indent string) string
type LogicalJoin ¶
type LogicalJoin struct {
BoundOn *BoundJoinPredicate
Left LogicalOperator
Right LogicalOperator
Kind JoinKind
OnText string
OnPredicate any // predicates.QueryPredicate when set
// OnExistsSubqueries carries EXISTS subqueries lifted from the ON clause
// (RFC-154 §5) BETWEEN two builder steps only: upgradeJoinOnPredicates
// parks them here and the block's last step, foldInnerOnExistsIntoWhere,
// moves them into the block's WHERE filter (an inner join's ON is a WHERE
// conjunct — Java's QueryVisitor folds it there). A join reaching the
// translator never carries one; the translator asserts that. Only ever
// populated for INNER joins (OUTER EXISTS-in-ON is rejected — RFC-154
// §5.2b).
OnExistsSubqueries []ExistsSubquery
}
LogicalJoin combines two children. Empty OnText means "no ON condition" (comma cross-join form — the outer WHERE provides the predicate). OnPredicate is the optional structured form (used by the catalog-aware walker); when non-nil, it takes precedence over OnText for Cascades lowering.
func NewJoin ¶
func NewJoin(left, right LogicalOperator, kind JoinKind, on string) *LogicalJoin
func NewJoinWithPredicate ¶
func NewJoinWithPredicate(left, right LogicalOperator, kind JoinKind, pred any) *LogicalJoin
NewJoinWithPredicate builds a LogicalJoin with a structured ON predicate.
func (*LogicalJoin) Children ¶
func (j *LogicalJoin) Children() []LogicalOperator
func (*LogicalJoin) Explain ¶
func (j *LogicalJoin) Explain(indent string) string
type LogicalLimit ¶
type LogicalLimit struct {
Input LogicalOperator
Limit int64
Offset int64
LimitValue values.Value
}
LogicalLimit caps the row count, optionally after skipping Offset. Negative Limit means "no limit" (pure offset).
LimitValue is an OPTIONAL runtime row cap (RFC-156 parameterized vector rank limit `... <= ?`): when non-nil the cap is evaluated at execution against the bound parameters and Limit is the no-cap sentinel (-1).
func NewLimit ¶
func NewLimit(input LogicalOperator, limit, offset int64) *LogicalLimit
func NewRuntimeLimit ¶
func NewRuntimeLimit(input LogicalOperator, limitValue values.Value, offset int64) *LogicalLimit
NewRuntimeLimit builds a LIMIT whose row cap is a runtime Value (evaluated at execution). The static Limit is the no-cap sentinel (-1).
func (*LogicalLimit) Children ¶
func (l *LogicalLimit) Children() []LogicalOperator
func (*LogicalLimit) Explain ¶
func (l *LogicalLimit) Explain(indent string) string
type LogicalOperator ¶
type LogicalOperator interface {
// Children returns the immediate child operators. Returning an
// empty slice (not nil) for leaf nodes keeps caller code free
// of nil checks.
Children() []LogicalOperator
// Explain returns an indented textual rendering of this node
// and its subtree. The indent argument is prefixed to this
// node's first line; children receive indent + " ".
//
// This is the stable surface Plan.Explain() exposes to
// frontend callers. Cascades physical plans will Explain()
// through their own impl; logical plans use this.
Explain(indent string) string
}
LogicalOperator is the root interface every logical operator satisfies. A LogicalOperator exposes its children (tree structure) and can render an indented text explanation of the subtree.
Operators are value-types (small, immutable once constructed). They are NOT identity-comparable — two structurally-identical Filter nodes should compare equal under a structural walker (implemented on top of this interface, not part of it).
func AttachedPlans ¶
func AttachedPlans(op LogicalOperator) []LogicalOperator
AttachedPlans returns the SUBQUERY plans attached to op beyond Children(): EXISTS/scalar plans on filters, projections, aggregates (HAVING) and joins (ON). Tree walkers that must see EVERY built operator (e.g. the CTE alias-arity validator) traverse Children() + AttachedPlans(). Keep this switch in lockstep with the attachment fields on the structs above — a new attachment field added without an arm here silently escapes whole-tree validation.
func FoldTransparentUnaryInput ¶
func FoldTransparentUnaryInput(op LogicalOperator) (LogicalOperator, bool)
FoldTransparentUnaryInput returns (input, true) when op is a fold-transparent unary operator — one the RFC-141 projected-EXISTS fold descends THROUGH to reach the existential filter without changing the row shape. Only Sort and Limit qualify (a Project/Join/Aggregate/Distinct/Union reshapes the rows and is NOT fold-transparent). This is the SINGLE source of truth for the transparency set: both the translator's `findExistsFilterUnderUnaryChain` (which folds the projection through the chain) and the generator's `existsFilterReachableForFold` (which rejects a projected EXISTS the fold cannot reach) consult it, so the two can never silently diverge. Returns (nil, false) for any non-transparent op.
type LogicalProject ¶
type LogicalProject struct {
Input LogicalOperator
Projections []string
Aliases []string // parallel to Projections; "" means no alias
ProjectedValues []values.Value // parallel to Projections; nil slot = walker declined
IsComputed []bool // parallel to Projections; true = expression, not plain column ref
// SQLNames preserves semantic name presence independently of physical
// aliases: an empty slot is unnamed, even when its emitted field is _N.
// nil means names derive from the projection's authored references/aliases.
SQLNames []string
// AliasMinted is parallel to Aliases: true = the alias in that slot was
// written by the MACHINERY, not by the user's `AS`. It is the provenance of
// the name, carried, because the name's SHAPE cannot carry it: the
// duplicated-bare-leaf dedup pins a projected reference's QUALIFIED
// spelling ("A.K") as the alias so two same-named datum keys stay
// distinguishable in the executor's row map, and a user is equally free to
// write `AS "A.K"`. A consumer that decides between them by inspecting the
// string gets the user's alias wrong, which is how `SELECT u.name AS
// "U.NAME"` came to report the label NAME.
//
// Java needs no such marker because its internal column for a duplicate is
// ANONYMOUS (Expressions.java:268-287) while the label is untouched; where
// it genuinely needs two names for one column it carries a SEPARATE
// construction-time field (Type.Record.Field.fieldStorageNameOptional,
// Type.java:2826-2827,2897-2899) rather than recovering provenance from a
// spelling later. Go's executor keys its row map by name, so the qualified
// key must stay — deleting it returns ONE column for `SELECT a.k, b.k` —
// and the provenance rides beside it instead.
//
// nil (or a short slice) reads as "user alias" for every slot it does not
// cover: a machinery mint is the exceptional case and states itself.
AliasMinted []bool
// AliasSources is parallel to AliasMinted. A present entry is the
// STRUCTURED authored source identity captured at the instant the machinery
// minted that slot's alias. It is not the later physical Value owner: a
// projection Value can be reanchored onto `_current` while the alias remains
// the datum key minted from source A. A short/nil vector means the source was
// not captured; consumers must never reconstruct it from alias text.
AliasSources []values.ProjectionAliasSource
// SQLNameAuthored is parallel to Projections: true = the SQL name the slot
// publishes was written by the statement — an explicit `AS`, or an
// un-aliased reference to a column whose own name was (an unnest's AS/AT
// alias, a derived table's or CTE's authored label). Aliases cannot say
// this: the builder fills an un-aliased reference's slot with the selected
// attribute's inherited name. A derived table or CTE over this projection
// publishes an authored slot as semantic.Column.SQLAuthored, which
// resolution compares exactly. nil (or a short slice) reads as NOT
// authored — the descriptor-name reading, which keeps the relaxed pass.
SQLNameAuthored []bool
// AggregateOutputOrdinals is the exact native [group keys..., aggregate
// calls...] input slot for each post-aggregate projection item. A negative
// entry marks a computed item whose Value tree is bound separately. nil
// means this is not the SQL aggregate-output boundary. Keeping this
// identity separate from Projections/Aliases prevents a group key and an
// aggregate alias with the same spelling from being rebound by name.
AggregateOutputOrdinals []int
// InputOrdinals is a complete positional projection contract for machinery
// boundaries that are created before their input quantifier exists (for
// example a CTE column-list rename). The logical node deliberately leaves
// the corresponding ProjectedValues nil; cascades translation resolves each
// slot against the real input quantifier's exact flowed object value. nil
// means no such positional boundary.
InputOrdinals []int
// AggregateSlots is parallel to Projections; true = the slot's value tree
// CONTAINS an aggregate. Captured pre-rewrite, where the *AggregateValue
// node is still present (rewriteAggregateValuesInTree destructively replaces
// it with a typed FieldValue). Read once by the INSERT…SELECT promotion guard
// to identify reliably-typed aggregate-result columns — plain columns are
// concrete-typed too (ResolveIdentifier), so type-presence cannot
// discriminate. A bridge until the Java end-state (PromoteValue projection
// nodes), which dissolves this marker.
AggregateSlots []bool
ScalarSubqueries []ScalarSubquery // uncorrelated scalar subquery plans (pre-evaluated)
// ProjectionRefs is parallel to Projections and carries the parse-tree
// SEGMENTS of a plain column item — the same triple SortKey and GroupKey
// already carry, for the same reason (RFC-197: qualification is FullId
// SEGMENT COUNT, never a scan of the rendered name).
//
// Projections is a RENDERING. `A.B` written as a qualified reference and
// `"A.B"` written as one quoted identifier are the same bytes, so a consumer
// that recovers the qualifier by slicing at the first dot cannot tell a
// reference to source A from a column literally so named — it manufactures a
// qualifier out of a name and resolves against the wrong row. The segments
// are what the parser actually saw, so they decide it.
//
// A zero entry means "no segments": a computed item, a sentinel slot, or a
// producer that has not been taught to capture them. Consumers must treat
// that as "unknown", never as "not qualified" — the ONLY safe reading of an
// absent segment triple is to fall back to whatever the rendered name
// supported before.
ProjectionRefs []ColumnRef
}
LogicalProject selects / renames columns and computes expressions. Each element of Projections is the canonical text of the projected expression or column name. Aliases (parallel slice) hold the output name; empty string means "use the underlying name."
ProjectedValues (parallel to Projections) carries resolved Value trees when the catalog-aware builder successfully walks the ANTLR expression. nil slots mean the walker declined (unsupported shape) — the Cascades translator treats nil as "cannot translate" and returns nil for the whole query. Non-nil slots are used directly as projection Values in the Cascades plan.
func NewProject ¶
func NewProject(input LogicalOperator, projs, aliases []string) *LogicalProject
func (*LogicalProject) Children ¶
func (p *LogicalProject) Children() []LogicalOperator
func (*LogicalProject) Explain ¶
func (p *LogicalProject) Explain(indent string) string
type LogicalScan ¶
type LogicalScan struct {
Source ScanSource
Table string
Alias string
// TablePath preserves normalized identifier segments from the SQL parse
// boundary. A quoted dotted name is one segment, not a template qualifier.
// Nil denotes legacy programmatic input; non-nil empty is malformed.
TablePath []string
// Binding is the scan's binding correlation name when its FROM alias
// DUPLICATES an earlier leg's at the same level ("" = the alias binds —
// every non-duplicate leg). Carried from the
// parser's single mint authority (assignFromLegBindingIDs); consumers
// read it via sourceBinding and never re-derive binding identity from
// the alias. The SQL alias stays the DISPLAY qualifier (sourceAlias).
Binding string
}
LogicalScan reads a single table. Empty Alias means "use the table name as the source alias."
func FindVisibleScan ¶
func FindVisibleScan(op LogicalOperator, alias string) *LogicalScan
FindVisibleScan resolves a FROM qualifier without entering a definition.
func NewScan ¶
func NewScan(table, alias string, tablePath ...string) *LogicalScan
NewScan constructs a LogicalScan. SQL builders supply captured tablePath segments; synthesized alias references supply exactly one literal segment. Omitting tablePath retains the legacy string-only programmatic interface.
func (*LogicalScan) Children ¶
func (*LogicalScan) Children() []LogicalOperator
func (*LogicalScan) Explain ¶
func (s *LogicalScan) Explain(indent string) string
type LogicalSingleton ¶
type LogicalSingleton struct{}
LogicalSingleton is a source containing one row with no public columns. Java's FROM-less source uses Explode([true]) with empty visible expressions; the translator hides that private BOOLEAN behind an empty-record projection. SELECT expressions belong to the ordinary LogicalProject above this source.
func NewSingleton ¶
func NewSingleton() *LogicalSingleton
NewSingleton constructs the one-row, zero-column FROM-less source.
func (*LogicalSingleton) Children ¶
func (*LogicalSingleton) Children() []LogicalOperator
func (*LogicalSingleton) Explain ¶
func (*LogicalSingleton) Explain(indent string) string
type LogicalSort ¶
type LogicalSort struct {
Input LogicalOperator
Keys []SortKey
}
LogicalSort sorts its child rows by the given keys.
func NewSort ¶
func NewSort(input LogicalOperator, keys []SortKey) *LogicalSort
func (*LogicalSort) Children ¶
func (s *LogicalSort) Children() []LogicalOperator
func (*LogicalSort) Explain ¶
func (s *LogicalSort) Explain(indent string) string
type LogicalUnion ¶
type LogicalUnion struct {
Inputs []LogicalOperator
Distinct bool
}
LogicalUnion ties together two (or more) children with UNION [ALL] semantics. Distinct = true applies a DISTINCT dedup across the union.
func NewUnion ¶
func NewUnion(inputs []LogicalOperator, distinct bool) *LogicalUnion
func (*LogicalUnion) Children ¶
func (u *LogicalUnion) Children() []LogicalOperator
func (*LogicalUnion) Explain ¶
func (u *LogicalUnion) Explain(indent string) string
type LogicalUnnest ¶
type LogicalUnnest struct {
// Segments is the un-flattened dotted name of the array source
// (`["T1","ARR1"]` for `T1.arr1`). Segment 0 names the in-scope outer
// source; the remaining segments name the array field on it. Kept
// un-flattened (no re-split of a joined string) so the translator
// resolves segment-by-segment against the scope.
Segments []string
// Binding carries the comma source's duplicate-alias binding id
// (see LogicalScan.Binding) so the
// TABLE-FIRST demotion (demoteQualifiedTableUnnest) can restore it on
// the demoted LogicalScan. A genuine unnest consumes the same identity;
// repeated SQL labels do not replace it or collapse element/ordinal
// slots (RFC-256).
Binding string
// Alias is the AS alias (`x` in `... AS x`) bound to each unnested
// element. Empty when the AS alias is omitted (AT-only form).
Alias string
// AtAlias is the AT ordinal alias (`ord` in `... AT ord`), empty when
// absent. Its presence makes the Explode WITH ORDINALITY.
AtAlias string
// correlation. A lateral FROM leg binds it against its preceding FROM
// prefix; a correlated primary source in EXISTS binds it against its outer
// scope. Carrying the value preserves the owner, exact type and ordinal
// path instead of resolving the syntax again during translation.
CorrelatedCollection values.Value
// EnclosingOwner records that a lateral FROM leg's collection resolved to a
// source of an ENCLOSING query, not of this FROM (`EXISTS (SELECT 1 FROM h,
// w.arr AS v)` over an outer w; Java's resolveCorrelatedIdentifier reads
// the operators including the outer ones). The binder sets it from the
// resolution; the translator explodes such a collection as it is, correlated
// to the outer query, and declines a collection its FROM's left side does
// not own unless this is set.
EnclosingOwner bool
}
LogicalUnnest is a lateral array UNNEST source in the FROM list (`FROM t, t.arr AS x [AT ord]`). It is the RIGHT child of a lateral LogicalJoin whose LEFT child is the source `t` it correlates to. The translator lowers it to an Explode of the correlated array field under a FlatMap of the outer source. Mirrors Java's `LogicalOperator.generateCorrelatedFieldAccess`. RFC-142 R5.
func FindOwnerUnnest ¶
func FindOwnerUnnest(op LogicalOperator, alias string) *LogicalUnnest
FindOwnerUnnest returns the LogicalUnnest in the outer sub-plan `op` whose element (AS) alias matches `alias` — the OWNER of a CHAINED lateral unnest (`FROM t, t.arr AS x, x.sub AS y`: the second unnest's segment-0 `x` names the first unnest's element). Mirrors FindOuterScanTable's walk (does not descend into a CTE/derived Body — a derived table is its own FROM scope, so a chain rooted inside a derived body is out of scope), but matches unnest element aliases instead of scans. nil when no such prior unnest.
The AT ordinal alias (`t.arr AS x AT o`) is deliberately NOT matched: `o` binds a scalar INTEGER ordinal, not a struct, so `o.sub` can never be a valid chain root. Matching it would misclassify `o.sub` as owned by this unnest and resolve `sub` against the ELEMENT's descriptor — emitting a plan that silently returns zero rows where Java rejects `o.sub` (a field access on a scalar). Only the AS element alias carries a chainable struct.
func (*LogicalUnnest) Children ¶
func (*LogicalUnnest) Children() []LogicalOperator
func (*LogicalUnnest) Explain ¶
func (u *LogicalUnnest) Explain(indent string) string
type LogicalUpdate ¶
type LogicalUpdate struct {
Target string
// TargetPath is the target's identifier segments (LogicalInsert.TablePath).
TargetPath []string
Sets []Assignment
Input LogicalOperator // the scan + filter producing target rows
}
LogicalUpdate updates Target rows matching Input with the per-col expression assignments in Sets.
func NewUpdate ¶
func NewUpdate(target string, sets []Assignment, input LogicalOperator) *LogicalUpdate
func (*LogicalUpdate) Children ¶
func (u *LogicalUpdate) Children() []LogicalOperator
func (*LogicalUpdate) Explain ¶
func (u *LogicalUpdate) Explain(indent string) string
type ScalarSubquery ¶
type ScalarSubquery struct {
Alias values.CorrelationIdentifier
Plan LogicalOperator
}
ScalarSubquery pairs a correlation alias with the logical plan for a scalar subquery `(SELECT MAX(v) FROM t2)`. Carried on LogicalFilter and LogicalProject so the Cascades translator can build inner plans. The executor pre-evaluates these and binds the scalar result under Alias before evaluating the outer plan's predicates/projections.
type ScanSource ¶
type ScanSource struct {
// contains filtered or unexported fields
}
ScanSource records both a selected producer and a physical lookup. The zero value is reserved for constructor input not yet adapted by BindCTESources.
func CTEScanSource ¶
func CTEScanSource(p *CTEProducer) ScanSource
func PhysicalScanSource ¶
func PhysicalScanSource() ScanSource
func (ScanSource) Producer ¶
func (s ScanSource) Producer() *CTEProducer
func (ScanSource) Resolved ¶
func (s ScanSource) Resolved() bool
type SortKey ¶
type SortKey struct {
Expr string // canonical text
Dir SortDir
NullsFirst bool
Value values.Value // resolved Value expression (nil = use text as FieldValue)
// Pos is a selected output's 1-based position; 0 means no slot selected.
// ORDER BY <n>, a resolved SELECT alias, and a resolved UNION output name
// use the same slot-binding mechanism. Authored numeric syntax remains in
// the parser's orderByClause, independently of this semantic address.
// The translator bakes a surviving Pos directly to the output slot, without
// reinterpreting its rendered spelling as another alias or expression.
Pos int
// AggregateOutputOrdinal is an exact address into the grouped input's
// native [keys..., calls...] row. The bool distinguishes native slot zero
// from an unset field. It is used when the visible aggregate Project is
// deliberately above ORDER BY.
AggregateOutputOrdinal int
HasAggregateOutputOrdinal bool
// AggregateOutputValueExact marks Value as a validated structural draft
// over the aggregate's grouping keys and calls. The translator binds that
// draft to its real aggregate-row owner; generic sort rebasing must not
// overwrite it by name.
AggregateOutputValueExact bool
// Bare/Qualifier/Qualified: parse-tree segments of a plain column
// reference key; zero values for positional and expression keys (their
// Expr is a rendering only). Qualification is FullId SEGMENT COUNT.
Bare string
Qualifier string
Qualified bool
// Segs is the FULL ordered segment list of the reference (`a.n.sk` ->
// [A N SK]). Resolution consumes Segs; Qualifier is a rendering that
// cannot say where one segment ends and the next begins, so a deeper
// reference resolved through it looks for a source named "A.N".
Segs []string
// BareRef marks a key whose source text is a plain ONE-segment column
// reference — the only shape SQL binds to an output alias. False for
// qualified references (`ORDER BY d.x`), aggregates and computed
// expressions: their canonical renderings ("X" after qualifier
// stripping, "SUM(S.SCORE)") are indistinguishable from a delimited
// alias's spelling, so alias-binding passes REQUIRE BareRef instead of
// inspecting Expr text.
BareRef bool
}
SortKey is one ORDER BY entry.
type TraversalOrder ¶
type TraversalOrder int
const ( TraversalAnyOrder TraversalOrder = iota TraversalLevelOrder TraversalPreOrder TraversalPostOrder )
The zero value is ANY — a recursion with NO traversal clause leaves the order to the planner (Java TraversalStrategy.ANY: both the level union and the DFS join implement, the cost model picks). An EXPLICIT `TRAVERSAL ORDER level_order` is TraversalLevelOrder and PINS the level union (Java's LEVEL gates the DFS rule off) — the two were once conflated, which made the explicit clause unable to force the plan.