query

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package query defines the planner/plan seam between the SQL frontend (database/sql driver, future gRPC server, REPL) and the SQL execution engine. Mirrors the role of Java's fdb-relational-core/recordlayer/query/Plan.java + AbstractEmbeddedStatement.executeInternal's 40-line dispatch.

A frontend holds a Generator (typically one per connection/session). For every SQL string:

plan, err := gen.Plan(ctx, sql)   // parse + analyze + plan
result, err := plan.Execute(ctx)  // run against the bound session

No frontend code touches the execution engine directly. All backend shapes live behind Generator + Plan and swap without changing callers.

Introduced in RFC 021 to let a Cascades Generator be swapped in behind this boundary. That migration is complete: Cascades is the sole Generator for queries and DML, and the original naive per-shape executor was removed in RFC-145 (only `execStatement`, for DDL, remains).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckBuriedExistentialPredicate

func CheckBuriedExistentialPredicate(root *expressions.Reference) error

CheckBuriedExistentialPredicate checks consumer ownership through boolean connectives and rejects unlowered scalar ExistsValues.

func CheckProjectedExistsFolded

func CheckProjectedExistsFolded(root *expressions.Reference) error

CheckProjectedExistsFolded is the RFC-141 §8 safety guard. Given the root Reference of a freshly translated (pre-planning) plan tree, it returns an *UnfoldedProjectedExistsError when any ExistsValue in the tree is positioned where its existential binding will NOT be live at eval time — the long-tail silent-wrong-result the projected-EXISTS fold's structural pattern-matching could otherwise let through.

Mechanism (two passes over the expression tree, mirroring Java's structural invariant that an ExistsValue is only correct inside the resultValue of the SelectExpression whose existential quantifier it reads):

  1. Build an ownership map: for every SelectExpression in the tree, for each existential quantifier it declares, record alias -> that SelectExpression. An existential alias is declared by exactly one SelectExpression (the one the translator attaches the NamedExistentialQuantifier to).

  2. For every expression in the tree, inspect the Value(s) it emits in its own scope and find every ExistsValue. For each, resolve the existential alias it reads (its QuantifiedObjectValue child's correlation) and require that the emitting expression IS the SelectExpression that owns that existential quantifier. If it is not (or no SelectExpression owns the alias at all), the binding is dead at this position -> reject.

Returns nil when every ExistsValue is correctly folded (the supported shapes: projected EXISTS / NOT EXISTS, correlated / non-correlated, alongside ORDER BY / LIMIT / a scalar subquery, and projected EXISTS over a JOIN in FROM — all fold the projection into the existential SelectExpression's result value).

func ExactLogicalOutputLabels

func ExactLogicalOutputLabels(
	op logical.LogicalOperator,
	md *recordlayer.RecordMetaData,
	enclosing *logical.CTERegistry,
) ([]string, error)

ExactLogicalOutputLabels returns the SQL OUTPUT LABELS of op's row — one per column of ExactLogicalResultType(op), in the same order.

The exact row's field NAMES are not those labels. A join row qualifies every leg column with its source alias (logicalLegFields) because the executor keys its row map by name and `A.K`/`B.K` must stay distinguishable; that spelling is a DATUM KEY. `SELECT * FROM A, B` returns columns named AID, K, ARR, BID, K — a duplicate among them is ordinary SQL, and a bare reference to it is 42702, not a reason to invent distinct names.

Publishing the datum keys where labels were wanted is not a cosmetic error: the semantic scope resolves `D.AID` against them, so a CTE over a two-table star registered A.AID and the reference failed with 42703 on a column the parser had seen perfectly well.

This walks the same structure as exactLogicalResultType and must stay the same width — every arm that changes the row's shape appears in both. The qualifier is REMOVED STRUCTURALLY, by not adding it, never by slicing at a dot: a column can genuinely be named "A.B" (quoted), and a consumer that recovers the qualifier from the rendered name cannot tell the two apart.

func ExactLogicalResultType

func ExactLogicalResultType(op logical.LogicalOperator, md *recordlayer.RecordMetaData) (values.Type, error)

ExactLogicalResultType derives the exact whole-object type flowed by op. It is used at the SQL-to-Value boundary, where an existential QOV must be typed before the Cascades translator creates its matching quantifier.

func ExactLogicalResultTypeWithCTEs

func ExactLogicalResultTypeWithCTEs(
	op logical.LogicalOperator,
	md *recordlayer.RecordMetaData,
	enclosing *logical.CTERegistry,
) (values.Type, error)

ExactLogicalResultTypeWithCTEs is ExactLogicalResultType with the ENCLOSING WITH bindings supplied by the caller, keyed UPPERCASE.

A logical tree does not always carry its own CTE wrapper: a derived-table body reading a WITH name builds to a bare LogicalScan of that name, which has no catalog descriptor and cannot be typed from the tree alone. The binding lives in the SEMANTIC scope at that point, so the caller that holds it passes it in rather than the derivation guessing.

func FieldTypeForFD

func FieldTypeForFD(fd protoreflect.FieldDescriptor) values.Type

FieldTypeForFD maps a protoreflect.FieldDescriptor to a values.Type, mirroring jdbcTypeNameForFD (pkg/relational/core/embedded/select_helpers.go). Repeated/map and non-UUID message fields collapse to values.UnknownType — 7.6 doesn't model nested/array element types for the anchored leg columns. Columns are nullable (the flowed leg row doesn't carry per-column NOT NULL constraints).

Delegates to values.FieldTypeForProtoField — see that function for why exactly one copy of this mapping may exist. The scan leaf typed here and the sargable match candidate's layout (executor.PositionalTypeForDescriptor) describe the same stored columns and feed the same planner decisions, so they must not be able to disagree.

func FindUnsupportedFunction

func FindUnsupportedFunction(op logical.LogicalOperator) string

FindUnsupportedFunction walks the logical plan tree and returns the name of the first ScalarFunctionValue that isn't in the supported set. Returns "" if all functions are supported.

func LogicalResultTypeAfterUnionPromotionWithCTEs

func LogicalResultTypeAfterUnionPromotionWithCTEs(
	op logical.LogicalOperator,
	md *recordlayer.RecordMetaData,
	enclosing *logical.CTERegistry,
) (values.Type, error)

LogicalResultTypeAfterUnionPromotionWithCTEs derives the row the existing UNION normalization will publish, without rewriting or rebinding the body. Unlike ExactLogicalResultTypeWithCTEs, compatible UNION inputs need not yet have identical types. Both paths derive all other operators identically.

func LowerExistsInput

LowerExistsInput lowers an already-bound, self-contained child exactly once. The resulting edge owns both the producer Reference and its exact result type. No parent registrations are published here.

func OutputLabelNames

func OutputLabelNames(labels []OutputLabel) []string

OutputLabelNames is the names of labels, in order; nil for nil.

func QuantifierColumnNames

func QuantifierColumnNames(labels []string) []string

QuantifierColumnNames is how Java names the columns of a row read through a quantifier's flowed type (SemanticAnalyzer.resolveTableFunction's Expressions.fromQuantifier, a recursive CTE's union): a name stays only where it is unique (Expressions.underlyingAsColumns), and once any column lacks one, every unnamed or auto-generated (`_<digits>`) name becomes the column's position (Type.Record.normalizeFields). An empty label is unnamed.

func TargetElementType

func TargetElementType(fd protoreflect.FieldDescriptor) values.Type

TargetElementType types what a field descriptor's VALUE is, with repetition already accounted for by the caller: applied to an array column's repeated field it yields the ELEMENT type, applied to a scalar or struct field it yields that field's type. Struct fields recurse through TargetTypeForFD, because a struct's fields are slots (an array field inside a struct is an array), not elements.

func TargetField

func TargetField(fd protoreflect.FieldDescriptor, ordinal int) values.Field

TargetField is a struct field as Java's Type.Record.Field.fromDescriptor (preserving names) has it: the user identifier, the protobuf number and the storage name.

func TargetTypeForFD

func TargetTypeForFD(fd protoreflect.FieldDescriptor) values.Type

TargetTypeForFD is the DML TARGET type of a column: the type a row constructor is pushed down against, Java's `targetField.getFieldType()` in ExpressionVisitor.parseRecordField (ExpressionVisitor.java:969). Unlike FieldTypeForFD (below) it descends: a struct column states its values.RecordType (named after the declared struct, fields in descriptor order), an array column its values.ArrayType with a real element type.

The two are separate on purpose and the separation is temporary in the design, not in intent: FieldTypeForFD types the FLOWED scan row, where collapsing structs and arrays to UnknownType is load-bearing today (the anchored-leg column types, index-on-source, derived unnest all read that collapse). RFC-204 §4.4/§4.5 unify them by making the flowed row carry the nested types; until the query surface and metadata pipeline can consume that, widening FieldTypeForFD would change plan-time typing for every query rather than for DML alone.

func TranslateToCascades

func TranslateToCascades(op logical.LogicalOperator) *expressions.Reference

TranslateToCascades converts a logical.LogicalOperator tree into a cascades RelationalExpression tree rooted in a Reference. This is the bridge between the SQL parser's logical plan and the Cascades optimizer.

Returns the root Reference suitable for passing to Planner.PlanWithContext(). Returns nil if the operator tree contains shapes that can't be translated (unsupported operators fall through to nil).

func UpdateResultType

func UpdateResultType(old, target values.Type) *values.RecordType

UpdateResultType is UpdateExpression's row: "old", the row before the update, and "new", the target record after it, nullable as Java's Type.Record.fromFields target type is.

func ValidateCTEAliasArities

func ValidateCTEAliasArities(op logical.LogicalOperator) error

ValidateCTEAliasArities walks the BUILT logical tree and applies the point-of-truth alias-arity check to EVERY LogicalCTE carrying column aliases — including CTEs that are never referenced (whose bodies translateCTE registers lazily and never descends into) and CTEs nested inside another CTE's body. Same exact-width rule as translateCTE's inline backstop; recursive CTEs are excluded (their seed/recursive arms have their own validation path).

Types

type BuriedExistentialPredicateError

type BuriedExistentialPredicateError struct{}

BuriedExistentialPredicateError reports an ExistsValue that was not lowered to a boolean predicate consumer before planning.

func (*BuriedExistentialPredicateError) Error

type DanglingExistentialPredicateError

type DanglingExistentialPredicateError struct {
	Alias values.CorrelationIdentifier
}

DanglingExistentialPredicateError signals a translated plan tree whose directly-handled existential predicate (`EXISTS(q)` / `NOT EXISTS(q)`) names a quantifier the predicate-bearing expression does not own. Such a marker has no subquery to peel into a semi-join; the NLJ rule finds no existential quantifier for it and the predicate is dropped, so the query returns every row the EXISTS should have excluded. It is a translator defect by construction — translateJoinWithExists once attached only the WHERE's existential quantifiers and left an ON-clause EXISTS marker dangling — and the guard refuses the plan rather than let the planner silently lose the predicate.

func (*DanglingExistentialPredicateError) Error

type Generator

type Generator interface {
	// Plan parses, semantically analyzes, and plans a SQL string.
	// Errors are typed via pkg/relational/api — callers should not
	// need to wrap them.
	Plan(ctx context.Context, sql string) (Plan, error)
}

Generator builds executable Plans from SQL strings. One Generator per logical session — it carries the session's catalog handle, current schema, and options.

type MultiPlan

type MultiPlan struct {
	Plans []Plan
}

MultiPlan wraps a sequence of Plans produced from a multi-statement SQL text (semicolon-separated). Execute runs them in order and returns a Result holding the SUM of RowsAffected (for Exec-style callers); the last Plan's Rows if any are exposed via Results(). This matches today's EmbeddedConnection.ExecContext aggregation: total modified rows across the batch, no intermediate result sets bubbled up.

func (*MultiPlan) Execute

func (m *MultiPlan) Execute(ctx context.Context) (Result, error)

Execute runs every child Plan in order, short-circuiting on the first error. Returns the aggregate RowsAffected; Rows is nil (multi-statement Exec doesn't expose intermediate row sets).

func (*MultiPlan) Explain

func (m *MultiPlan) Explain() string

Explain returns every child's explanation joined by ';\n'.

func (*MultiPlan) IsUpdate

func (m *MultiPlan) IsUpdate() bool

IsUpdate returns true iff every child Plan is an update plan. A mixed batch (e.g. DDL + SELECT) is treated as non-update so the last Rows-producing plan would be reachable via a future Results() iteration. In practice today's driver doesn't send mixed batches through Exec.

type OutputLabel

type OutputLabel struct {
	Name     string
	Authored bool
}

OutputLabel is one SQL name a logical row publishes, with its provenance. Authored reports that the statement's SQL text wrote the name — a SELECT-list `AS`, a CTE column list, an unnest's AS or AT alias — rather than that it passes a descriptor's column name through. The semantic scope resolves an authored name exactly (semantic.Column.SQLAuthored); only a descriptor spelling takes the relaxed pass's fold.

func ExactLogicalOutputLabelsAuthored

func ExactLogicalOutputLabelsAuthored(
	op logical.LogicalOperator,
	md *recordlayer.RecordMetaData,
	enclosing *logical.CTERegistry,
) ([]OutputLabel, error)

ExactLogicalOutputLabelsAuthored is ExactLogicalOutputLabels with each label's provenance: the same walk, the same width.

type Plan

type Plan interface {
	Execute(ctx context.Context) (Result, error)
	IsUpdate() bool
	Explain() string
}

Plan is a ready-to-execute representation of a SQL statement. One Plan per SQL statement; a multi-statement SQL text produces a MultiPlan (see below).

Execute returns a Result whose concrete shape depends on the statement kind:

  • SELECT / SHOW / read-only: Result.Rows is non-nil; Result. RowsAffected is zero.
  • INSERT / UPDATE / DELETE: Result.RowsAffected counts the modified rows; Result.Rows is nil.
  • DDL (CREATE / DROP): Result.Rows is nil; Result.RowsAffected is zero.

IsUpdate distinguishes mutation plans so the driver knows whether to return driver.Rows vs driver.Result at the boundary. Matches Java's Plan.isUpdatePlan().

Explain returns a textual description of the plan. Today the naive Generator returns the canonical SQL text; future Cascades plans will return a plan tree that is stable enough for the RFC-022 §4.-1 plan-equivalence harness to diff against Java's. Empty string is a valid value for plans that cannot produce a useful description.

type PlanFunc

type PlanFunc struct {
	ExecFn    func(ctx context.Context) (Result, error)
	UpdateFn  func() bool
	ExplainFn func() string
}

PlanFunc is a convenience adapter: a Plan whose Execute delegates to a closure. Used to wrap non-Cascades code paths (e.g. the executor-free INFORMATION_SCHEMA system-table handler and the explain-only renderer) as Plan implementations without duplicating logic.

Post-Phase-1c this adapter becomes obsolete — physical operator types implement Plan directly. Kept here during the transition so the frontend seam is stable before the executor is split.

func (*PlanFunc) Execute

func (p *PlanFunc) Execute(ctx context.Context) (Result, error)

Execute runs the wrapped closure.

func (*PlanFunc) Explain

func (p *PlanFunc) Explain() string

Explain returns ExplainFn's result, or empty when ExplainFn is nil.

func (*PlanFunc) IsUpdate

func (p *PlanFunc) IsUpdate() bool

IsUpdate returns UpdateFn's result, or false when UpdateFn is nil.

type Result

type Result struct {
	// Rows is non-nil for SELECT-shaped plans. Nil for DML/DDL.
	Rows driver.Rows
	// RowsAffected is the update count for DML. Zero for SELECT/DDL.
	RowsAffected int64
}

Result is the output of a Plan execution. Exactly one of Rows / RowsAffected carries the payload; which one is set is determined by Plan.IsUpdate().

type ScalarSubqueryPlan

type ScalarSubqueryPlan struct {
	Alias values.CorrelationIdentifier
	Plan  logical.LogicalOperator
}

ScalarSubqueryPlan pairs a correlation alias with a logical operator tree for a scalar subquery. Collected during translation and passed to the executor for pre-evaluation.

func TranslateIndexDefinitionToCascades

func TranslateIndexDefinitionToCascades(op logical.LogicalOperator, md *recordlayer.RecordMetaData) (*expressions.Reference, []ScalarSubqueryPlan, error)

TranslateIndexDefinitionToCascades translates an index definition's query. Java's QueryVisitor leaves an index definition's COUNT unadjusted (isForDdl), so its aggregate stays the index's aggregate.

func TranslateSubqueryToCascades

TranslateSubqueryToCascades translates a subquery planned on its own (a scalar subquery, an EXISTS body): not a top level, so no sort is stated above it.

func TranslateToCascadesWithError

TranslateToCascadesWithError is TranslateToCascadesWithSubqueries plus an explicit translation error. A non-nil error carries a specific SQL error code (e.g. ErrCodeWrongObjectType for AT-ordinality on a non-array source, RFC-142) that a bare nil ref (untranslatable → UNSUPPORTED_QUERY) cannot. The caller surfaces it verbatim instead of the generic "could not plan".

func TranslateToCascadesWithSubqueries

func TranslateToCascadesWithSubqueries(op logical.LogicalOperator, md *recordlayer.RecordMetaData) (*expressions.Reference, []ScalarSubqueryPlan)

TranslateToCascadesWithSubqueries is like TranslateToCascades but also returns any scalar subquery plans collected during translation. These must be planned independently and pre-evaluated by the executor before running the main plan.

md carries the record metadata used to source join-leg columns when building the source-anchored join result value (RFC-077 7.6). Pass nil to keep the legacy opaque-seed behavior — the no-md callers today are TranslateToCascades (used for scalar-subquery translation, which has no md in scope) and DML translation. (Tests pass real md where they exercise anchoring.) The scan leaf is NEVER typed from md (it stays Type.AnyRecord/UnknownType, matching Java — see RFC-077 v3 amendment); md is consulted only to enumerate a leg's columns for the anchored RecordConstructor.

type UnfoldedProjectedExistsError

type UnfoldedProjectedExistsError struct {
	// Alias is the existential correlation the offending ExistsValue reads.
	Alias values.CorrelationIdentifier
}

UnfoldedProjectedExistsError signals that a translated plan tree carries a projected ExistsValue that is NOT folded into the result value of the SelectExpression owning its existential quantifier — i.e. the boolean would be evaluated ABOVE the FlatMap, without the existential binding live, and ExistsValue.Evaluate would silently return false (or, via a QOV fallback, phantom-true). That is a silent wrong result, so the production path rejects such a plan with ErrCodeUnsupportedQuery rather than shipping wrong rows (RFC-141 §8 safety guard).

The cleanly-rejected shapes are the projected-EXISTS long tail the fold does not yet recognize (e.g. multiple existential quantifiers in one query, or a projection over a shape findExistsFilterUnderUnaryChain cannot fold through). They are correctness-preserving rejections, never wrong answers.

func (*UnfoldedProjectedExistsError) Error

Directories

Path Synopsis
Package ddl holds the materialized-view index generator — the Go port of Java's MaterializedViewIndexGenerator (RFC-202).
Package ddl holds the materialized-view index generator — the Go port of Java's MaterializedViewIndexGenerator (RFC-202).
Package expr is the parse-tree → values.Value resolver.
Package expr is the parse-tree → values.Value resolver.
Package logical holds the Phase 3 (TODO.md §"Phase 3 — Semantic analysis") logical-operator hierarchy.
Package logical holds the Phase 3 (TODO.md §"Phase 3 — Semantic analysis") logical-operator hierarchy.
Package semantic is the Go port of Java's `com.apple.foundationdb.relational.recordlayer.query.SemanticAnalyzer` plus related Identifier / Expression / reference-resolution helpers.
Package semantic is the Go port of Java's `com.apple.foundationdb.relational.recordlayer.query.SemanticAnalyzer` plus related Identifier / Expression / reference-resolution helpers.
rlcatalog
Package rlcatalog adapts the Record Layer's `RecordMetaData` into the `semantic.Catalog` interface.
Package rlcatalog adapts the Record Layer's `RecordMetaData` into the `semantic.Catalog` interface.

Jump to

Keyboard shortcuts

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