query

package
v0.0.0-...-2e4c5eb Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2026 License: Apache-2.0 Imports: 16 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 AssertUnnestLegMintCensus

func AssertUnnestLegMintCensus(w io.Writer, executorDottedNames []string) bool

AssertUnnestLegMintCensus checks the one claim this census exists to keep honest: the names this mint produces and the names the EXECUTOR's dotted leg-column reader answers on are DISJOINT sets.

executorDottedNames is the dotted-hit name list from executor.LegColumnProvenanceDottedNames — threaded in rather than imported, because the executor package must not depend on the translator and this assertion needs both populations in one place.

WHY A DISJOINTNESS ASSERTION AND NOT A FLOOR. The booked acceptance condition for retiring the executor's dotted arm is that its hit count reaches zero, and that condition was booked against converting THIS mint. Disjointness is the measurement that says the condition is not reachable from here: the reader's live hits are produced somewhere else, so this mint could be deleted outright and the reader would answer exactly as often as before. A negative result of that shape is load-bearing — it is what reclassifies the conversion — so it is pinned rather than written down.

func CheckBuriedExistentialPredicate

func CheckBuriedExistentialPredicate(root *expressions.Reference) error

CheckBuriedExistentialPredicate is the RFC-141 R4 convergence backstop for WHERE EXISTS (P1a). Given the root Reference of a freshly translated (pre-planning) plan tree, it returns a *BuriedExistentialPredicateError when any predicate-bearing expression carries an existential predicate that is NOT in a directly-handled position — i.e. an ExistentialValuePredicate buried inside a predicate that is neither the top-level existential nor a single-NOT-wrapped existential.

EXISTS can appear at any depth in a WHERE predicate tree. Only a top-level (or single-NOT-wrapped) existential is the semi-join shape the NLJ rule lowers to a FirstOrDefault + residual filter; everything else falls into the regular bucket where the empty FOD's NULL default is never dropped and every outer row passes. Rather than point-handle each wrapper shape (which never converges), this structurally DETECTS any buried existential and rejects cleanly.

Per predicate-bearing expression (SelectExpression / LogicalFilterExpression), each top-level predicate is classified:

  • IsExistentialPredicate(p) → directly handled (bare EXISTS). OK.
  • IsNotExistentialPredicate(p) → directly handled (single-NOT NOT-EXISTS). OK.
  • otherwise, if p's subtree CONTAINS an ExistentialValuePredicate anywhere (predicates.ContainsExistentialPredicate) → buried → REJECT.

Returns nil when every existential predicate is in a directly-handled position (the supported WHERE-EXISTS / NOT-EXISTS shapes, including alongside ordinary non-existential conjuncts, multi-table inners, and projected EXISTS).

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 (its resultValue, or — for a LogicalProjectionExpression whose resultValue is the inner's flowed object, not the projection — its projected values) 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 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 FormatUnnestLegMintCensus

func FormatUnnestLegMintCensus() string

FormatUnnestLegMintCensus renders the census for a harness to log.

func RecordUnnestLegMintArm

func RecordUnnestLegMintArm(site UnnestLegMintSite, arm UnnestLegMintArm)

RecordUnnestLegMintArm counts ONE arm taken at a seed-tested branch point. Callers must guard on values.LegIdentityCensusEnabled().

func RecordUnnestLegMintBranchReached

func RecordUnnestLegMintBranchReached(site UnnestLegMintSite)

RecordUnnestLegMintBranchReached counts ONE arrival at a seed-tested branch point, BEFORE any arm is chosen. Callers must guard on values.LegIdentityCensusEnabled().

It is the INDEPENDENT denominator for the arm matrix. Summing the arms instead is true by construction and cannot see an arm that was added without a counter — see the UnnestLegMintArm doc.

func RecordUnnestLegMintCall

func RecordUnnestLegMintCall(site UnnestLegMintSite)

RecordUnnestLegMintCall counts ONE invocation of the name-keyed rebase at a site, whether or not it rewrites anything. Callers must guard on values.LegIdentityCensusEnabled().

Counted separately from the NAMES because a site can be reached constantly and mint nothing (no reference in the predicate names an outer leg), and the two facts have opposite meanings for a conversion: the first says the arm is live, the second says it produces the text a downstream reader decides on.

func RecordUnnestLegMintName

func RecordUnnestLegMintName(site UnnestLegMintSite, name string)

RecordUnnestLegMintName counts ONE minted qualified name at a site. Callers must guard on values.LegIdentityCensusEnabled().

func RecordUnnestLegOrdinalTwinCall

func RecordUnnestLegOrdinalTwinCall()

RecordUnnestLegOrdinalTwinCall counts ONE invocation of rebaseUnnestOuterLegPredicateOrdinal, from any caller.

It is the DENOMINATOR the per-arm counts are read against, and it is counted inside the twin rather than summed from the arms for the reason the fold-step1 census states about its own independent denominator: a sum over the recorded arms is true by construction and cannot see a caller that reaches the twin without passing a branch point this census instruments.

The twin has THREE call sites: the buried `seedWindowed` arm, the chained `ordinalSeed` arm, and `bakeInnerExistsPredicateOrdinal`. This census instruments the first two directly as ARMS; the third is reached only through the two planTimeBake arms, so its calls are attributed to no arm and show up as the gap between this total and the summed ordinal-twin arms. (An earlier revision of this comment said "four callers", counting the two planTimeBake arms as separate callers of the twin — they are two callers of `bakeInnerExistsPredicateOrdinal`, which is one caller of the twin.)

func RejectUnnestAliasCollisions

func RejectUnnestAliasCollisions(op logical.LogicalOperator) error

RejectUnnestAliasCollisions applies unnestAliasReject to every lateral unnest in a logical tree, so the binding error surfaces at FROM-scope analysis rather than at translation.

The ORDER is the point, not the reach. `FROM t, t.arr AS X AT X` binds two different things — the element and the ordinal — to one range-variable name, and Java's visitAtomTableItem rejects that where the binding is made, before any SELECT-list reference is resolved against it. Left to translation, the duplicate binding stays live long enough for the semantic scope to see one source carrying the name twice and answer the reference with an ambiguity (42702) — a true statement about a scope that should never have been built, and the wrong error for the query.

func ResetUnnestLegMintCensus

func ResetUnnestLegMintCensus()

ResetUnnestLegMintCensus clears the counters.

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 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 UnnestLegMintArms

func UnnestLegMintArms() ([unnestLegMintSiteCount][unnestLegMintArmCount]int, [unnestLegMintSiteCount]int, int)

UnnestLegMintArms reports the per-site arm matrix and the twin's independent total.

func UnnestLegMintCensus

func UnnestLegMintCensus() (calls, mints [unnestLegMintSiteCount]int, names []string)

UnnestLegMintCensus reports per-site calls, per-site mints, and the distinct minted names.

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 signals that a translated plan tree carries a WHERE existential predicate (an ExistentialValuePredicate) buried under a wrapper that is NOT a directly-handled semi-join shape — i.e. it is neither a top-level existential nor a single-NOT-wrapped existential. Such a predicate falls into the regular-predicate bucket of the NLJ rule's implementExistentialSelect / implementJoinWithExistential, where the empty FirstOrDefault inner emits its NULL default that no residual filter removes, so EVERY outer row silently passes (a silent wrong result). The production path rejects such a plan with ErrCodeUnsupportedQuery rather than ship wrong rows (RFC-141 R4 convergence backstop, P1a).

The cleanly-rejected shapes are the wrapped-WHERE-EXISTS long tail: any existential reachable only through a wrapper the rule's IsExistentialPredicate / IsNotExistentialPredicate routing does not recognise — `WHERE NOT (NOT EXISTS(...))`, `WHERE EXISTS(...) OR p`, deeper AND/OR/NOT nesting. A plain `WHERE EXISTS` / `WHERE NOT EXISTS` (top-level or single-NOT-wrapped) is the directly-handled shape and is NOT rejected.

func (*BuriedExistentialPredicateError) 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 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 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

type UnnestLegMintArm

type UnnestLegMintArm int

UnnestLegMintArm is WHICH arm a seed-tested branch point took.

It exists because a per-site call count of ZERO at a `!seedWindowed` else-branch is AMBIGUOUS on its own, and the ambiguity has opposite consequences. "Never reached" says the shape is not planned; "reached, took the ordinal arm" says the shape IS planned and its conversion has already happened on the other side of the branch. Both readings support demoting a conversion booked against the name arm, and they imply different follow-ups — so the census must not leave the reader to pick one.

The arm is recorded AT the arm, and the branch's REACH is recorded INDEPENDENTLY at the branch point, before any arm is chosen. Both halves are necessary and the second one was missing when this census first shipped: the renderer computed the branch total AS the sum of the arms, so an arm added without a `RecordUnnestLegMintArm` call would silently SHRINK the printed `reached N` instead of showing up as a gap. That is the summed denominator this RFC's own text calls "true by construction", one field over from the twin counter that was given a real one.

It bites hardest at the joinPredicate branch, where the census guard doubles as the third arm (`else if jpCensus`): inserting an `else if` ahead of it steals silently from `leg-relative`. With an independent reach counter that theft is an ARM PARTITION FAIL, not a quieter number.

const (
	// UnnestLegMintArmName: the branch took the name-keyed rebase — the arm the
	// mint counters above measure.
	UnnestLegMintArmName UnnestLegMintArm = iota

	// UnnestLegMintArmOrdinalTwin: the branch took
	// rebaseUnnestOuterLegPredicateOrdinal. This is the reading that turns a zero
	// on the name arm from "dead shape" into "already converted here".
	UnnestLegMintArmOrdinalTwin

	// UnnestLegMintArmPlanTimeBake: the branch took the E-1a plan-time bake
	// (bakeInnerExistsPredicateOrdinal), which reaches the ordinal twin one level
	// down. Counted apart from the twin arm because the two are different
	// DECISIONS that happen to share a callee, and folding them would report an
	// inner-cluster bake as a seed-test outcome.
	UnnestLegMintArmPlanTimeBake

	// UnnestLegMintArmLegRelative: the JoinPredicate channel's windowed,
	// non-plan-time-bake fall-through, which rebases NOTHING and leaves the refs
	// leg-relative for the executor's below-FOD hoist. It is an arm of the branch
	// and therefore counted; without it the branch's arms do not partition and a
	// zero elsewhere cannot be read.
	UnnestLegMintArmLegRelative
)

func (UnnestLegMintArm) String

func (a UnnestLegMintArm) String() string

type UnnestLegMintSite

type UnnestLegMintSite int

UnnestLegMintSite is one call site of the name-keyed `rebaseUnnestOuterLegPredicate`. The five partition its callers, and they are named for WHY they reach it rather than for their line numbers, which move.

const (
	// UnnestLegMintSiteNonChainedMerge is the ELSE of the chained-unnest check
	// in the filter-over-unnest merge. It applies no seed test at all — the
	// `ordinalSeed` test lives INSIDE the chained arm, on the other side of this
	// branch — so no seed-gate flip converts it.
	UnnestLegMintSiteNonChainedMerge UnnestLegMintSite = iota

	// UnnestLegMintSiteAnchoredNonExists is the plain non-chained unnest merge's
	// anchored (not admitted to the gather / no record) arm, which the code
	// names "the correct and now ONLY domain of the name-keyed rebase". Also no
	// seed test.
	UnnestLegMintSiteAnchoredNonExists

	// UnnestLegMintSiteBuriedNotWindowed is the BURIED subquery-internal
	// outer-only filter's `!seedWindowed` else-branch. Its ordinal twin
	// (rebaseUnnestOuterLegPredicateOrdinal) is selected on the other side.
	UnnestLegMintSiteBuriedNotWindowed

	// UnnestLegMintSiteJoinPredNotWindowed is the EXISTS JoinPredicate channel's
	// `!seedWindowed` arm. Same twin on the other side.
	UnnestLegMintSiteJoinPredNotWindowed

	// UnnestLegMintSiteChainedNameModel is the chained rebase's name-model
	// fallback, taken when the chained seed is not ordinal.
	UnnestLegMintSiteChainedNameModel
)

func (UnnestLegMintSite) String

func (s UnnestLegMintSite) String() string

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