cascades

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

derivations_evaluator.go — evaluates DerivationsProperty for physical plan wrapper expressions. Mirrors Java's DerivationsProperty.DerivationsVisitor.

The evaluator walks the expression tree bottom-up, computing properties.Derivations for each node. For each wrapper type it computes what the result values and local values are by inlining child result values into the current node's value expressions (decorrelation).

This file answers a SARGABILITY question: given a key coordinate's authoritative PHYSICAL tuple type, what can a scan range bind there — can the coordinate be probed exactly, must the predicate stay a residual filter, and how many physical ranges does an equality open? The executor's range binder hard-requires these types (see scan_range_binding.go's validateAuthoritativeScanPhysicalType, which errors on an unknown one), so they are supplied from index metadata rather than inferred from comparands.

It is NOT the authority on ORDERING claims. Whether a coordinate may extend the order a plan advertises is answered by values.ColumnCanExtendOrderingClaim (see values/ordering_claim.go), with plans.EqualityPinsSinglePhysicalKey as its narrower companion for equality-bound coordinates.

The two are near-siblings and easy to confuse because they share a cause — a FLOAT/DOUBLE coordinate whose physical key order diverges from its logical order. Neither subsumes the other, and they may legitimately disagree: an equality on 0.0 opens TWO physical ranges (so it pins no single key and carries no order) while remaining perfectly sargable as a two-range probe. Deriving one from the other is how a plan comes to claim an order its scan does not deliver.

Go extension — no Java equivalent.

Java's RemoveSortRule (ImplementSortRule in Go) eliminates sorts via index ordering or fails. This rule provides an in-memory fallback: when no index can satisfy the ORDER BY, materialize and sort.

Registered alongside ImplementSortRule. Both match LogicalSortExpression. Cost model ensures index-based elimination is preferred — the in-memory sort only wins when it's the sole alternative.

Index

Constants

View Source
const (
	FunctionKindOrderAscNullsFirst  = "order_asc_nulls_first"
	FunctionKindOrderAscNullsLast   = "order_asc_nulls_last"
	FunctionKindOrderDescNullsFirst = "order_desc_nulls_first"
	FunctionKindOrderDescNullsLast  = "order_desc_nulls_last"
)

Order-function column tags — the four OrderFunctionKeyExpression names (OrderFunctionKeyExpressionFactory.java:44-48: "order_" + Direction lowercased). A key column carrying one stores the TupleOrdering encoding of its field, so its candidate-side Value is ToOrderedBytesValue(field, direction) (OrderFunctionKeyExpression.toValue, OrderFunctionKeyExpression.java:99-103) and its matched ordering direction is the function's, not the tuple-natural ascending.

View Source
const (
	// ReasonAbsent is the real defect: no member of the group equals the
	// child the plan executes.
	ReasonAbsent = "child absent from group"
	// ReasonEmptyGroup is a mis-seeded reference — the quantifier ranges
	// over a group with no final members at all.
	ReasonEmptyGroup = "group has no final members"
	// ReasonNoQuantifier is a plan child with no corresponding quantifier:
	// structural plumbing the memo never modelled.
	ReasonNoQuantifier = "plan child has no quantifier"
)

Reasons. A group holding MULTIPLE members including the plan's child is Cascades working correctly (alternatives), not a defect — conflating that with a true miss is precisely the error RFC-183 §13 corrects.

View Source
const CardinalityUnknown int64 = -1

CardinalityUnknown is the sentinel value for unknown cardinality, mirroring Java's Cardinality.unknownCardinality().

View Source
const DefaultMaxNumConjuncts = 9

DefaultMaxNumConjuncts is the maximum number of OR-predicate conjuncts that PredicateToLogicalUnionRule will attempt to convert to DNF. Beyond this limit, the combinatorial explosion of DNF terms is too expensive. Java's default is 9 (510 combinations).

View Source
const FunctionKindCardinality = "cardinality"

FunctionKindCardinality is the columnFunctions entry marking a key column as CARDINALITY(field). Matches the "cardinality" function-key name on the record-layer side so the bridge is name-stable across the two layers.

View Source
const NormalizerDefaultSizeLimit = 1_000_000

NormalizerDefaultSizeLimit is BooleanPredicateNormalizer.DEFAULT_SIZE_LIMIT (BooleanPredicateNormalizer.java:117) — the limit the default DNF/CNF normalizer instances carry.

View Source
const OpaquePredicateSignatureMarker = "p!opaque"

OpaquePredicateSignatureMarker is the signature a sparse index carries when its predicate is a programmatic Go closure rather than a serialized proto.

It lives here, not in the record layer, because the record layer imports the planner and not the other way round — and both sides must agree on the exact bytes or companion matching silently changes meaning. The record layer's PredicateSignature produces it; SamePredicateSignature there and samePredicateSignature here both refuse it.

Variables

View Source
var ErrPlannerAlreadyUsed = plannerErr("planner: PlanWithContext may be called only once; construct a new Planner for each planning attempt")

ErrPlannerAlreadyUsed signals that PlanWithContext was called more than once on the same Planner. A Planner owns one planning attempt; callers must construct a fresh Planner to retry after either success or failure.

View Source
var ErrPlannerCapHit = plannerErr("planner: MaxTasks cap hit before convergence")

ErrPlannerCapHit signals that PlanWithContext exited via the MaxTasks cap rather than convergence. Callers should treat this as a non-termination indicator and report.

View Source
var ErrPlannerNilContext = plannerErr("planner: nil context; use context.Background when no cancellation is required")

ErrPlannerNilContext signals a programmer error at the planning API boundary. Callers that intentionally have no deadline should pass context.Background rather than nil.

View Source
var ErrPlannerQueueCapHit = plannerErr("planner: MaxTaskQueueSize cap hit — task queue exceeded the configured bound")

ErrPlannerQueueCapHit mirrors Java's "Maximum task queue size was exceeded" RecordQueryPlanComplexityException (CascadesPlanner.isTaskQueueSizeExceeded).

View Source
var ErrPlannerRoundCapHit = plannerErr("planner: exploration round cap hit — a Reference kept producing new members after 100 rounds (rule-cycle divergence)")

ErrPlannerRoundCapHit signals a Reference still inserting new exploratory members after the round cap — a rule-cycle divergence the memo dedup should have collapsed (RFC-180 I2). A planner bug indicator, never load.

View Source
var ErrPlannerRuleMatchCapHit = plannerErr("planner: MaxNumMatchesPerRuleCall cap hit — one rule invocation produced more matches than the configured bound")

ErrPlannerRuleMatchCapHit mirrors Java's "Maximum number of matches per rule call has been exceeded" (CascadesPlanner.isMaxNumMatchesPerRuleCallExceeded).

View Source
var OrdinalLayoutConstraintKey = &PlannerConstraint[[]plans.OrdinalLayoutRequirement]{name: "ordinalLayout"}

OrdinalLayoutConstraintKey accumulates the exact physical input layouts required by finalized parents of a group. OptimizeInputs pushes one positional requirement per child edge; OptimizeGroup retains the cheapest final satisfying every accumulated requirement instead of allowing the group's single global winner to erase a costlier compatible alternative.

View Source
var ReferencedFieldsConstraintKey = &PlannerConstraint[*ReferencedFields]{name: "referencedFields"}

ReferencedFieldsConstraintKey is the constraint key for referenced fields. Pushed top-down by PushReferencedFieldsThrough* rules to inform downstream operators which columns/fields are actually needed. Ports Java's ReferencedFieldsConstraint.REFERENCED_FIELDS.

View Source
var RequestedOrderingConstraintKey = &PlannerConstraint[[]*properties.RequestedOrdering]{name: "requestedOrdering"}

RequestedOrderingConstraintKey is the constraint key for requested orderings.

Functions

func AddPartialMatchForCandidate

func AddPartialMatchForCandidate(
	ref *expressions.Reference,
	candidate MatchCandidate,
	match PartialMatch,
) bool

AddPartialMatchForCandidate stores a PartialMatch for the given MatchCandidate on the Reference. Typed wrapper around Reference.AddPartialMatch. Mirrors Java's Reference.addPartialMatchForCandidate.

Semantic dedup: Reference.AddPartialMatch dedups only by pointer identity, but matching rules re-fire and reconstruct the SAME logical match. Endpoint-only dedup is too coarse, however: partial matching can produce several valid alias maps, child selections, and bindings for one (queryExpression, candidateRef) pair. Drop only an exact semantic re-add; retain alternatives whose downstream matching or compensation metadata differs.

func AdjustFixedBindings

func AdjustFixedBindings(parts []properties.ProvidedOrderingPart, isReverse bool) []properties.ProvidedOrderingPart

AdjustFixedBindings adjusts the sort order of FIXED ordering parts to match the resolved comparison direction. If the comparison is reverse, fixed parts get descending; otherwise ascending. Mirrors Java's RecordQuerySetPlan.adjustFixedBindings.

func AdjustMatches

func AdjustMatches(rootRef *expressions.Reference)

AdjustMatches walks all References reachable from rootRef and, for every existing PartialMatch, attempts to absorb candidate-side-only expressions one level up from the matched candidate ref. On success, a new PartialMatch with AdjustedMatchInfo is stored on the same query Reference but pointing to the parent candidate Reference.

This is the Go equivalent of Java's AdjustMatchRule, which fires on PartialMatches via a TransformPartialMatch task. In Go, AdjustMatches is called explicitly after MatchLeafRule and MatchIntermediateRule have run, rather than being scheduled as a rule (see also AdjustPartialMatchesForRef at the data-access consumption boundary).

Ports Java's com.apple.foundationdb.record.query.plan.cascades.rules.AdjustMatchRule.

func AdjustPartialMatchesForRef

func AdjustPartialMatchesForRef(ref *expressions.Reference)

AdjustPartialMatchesForRef runs the AdjustMatchRule absorption loop on a single Reference's partial matches until stable. Each round attempts to absorb a candidate-side parent expression (SelectExpression → MatchableSortExpression) onto each existing PartialMatch, producing adjusted matches that carry e.g. matched ordering parts.

This is the per-ref unit of Java's event-driven AdjustMatchRule. It is invoked both by the bottom-up AdjustMatches walk (for REWRITING-phase matches) AND by pushDataAccessTasks just before it consumes a ref's matches — PLANNING-phase matches are seeded during exploration, after the phase-start AdjustMatches walk, so they must be adjusted at consumption time or their ordering parts (needed to satisfy a requested ordering and eliminate an in-memory sort) are never computed.

func AllAttributesExcept

func AllAttributesExcept(except ...*properties.ExpressionProperty) []*properties.ExpressionProperty

AllAttributesExcept returns plan properties excluding the specified ones.

func AssertMergeSlotTypingCensus

func AssertMergeSlotTypingCensus(w io.Writer, c MergeSlotTypingCounts, floor int) bool

AssertMergeSlotTypingCensus checks the census against `floor` slots, reports its reasoning to w, and returns whether it FAILED.

The polarity matches its siblings and the censusGate contract (census_gate_reporting_test.go): run returns FAILED, not OK. Getting it backwards makes a clean census report as a failure, which is how this was caught — the gate fired on a run reporting 22,362 slots with a holding partition and zero untyped. That total is one SAMPLE of a population that moves with memo exploration order (~22.4k; see positional_merge.go), which is why the floor below is an order of magnitude under it rather than an equality.

Two directions, and they are not the same alarm. The PARTITION identity going wrong means the instrument miscounts, so every share it reports is meaningless. UNTYPED going non-zero means a live slot lost its row type, which is the silent zero-rows defect. The floor guards the third case: a collapsed denominator makes `Untyped == 0` true for the wrong reason.

func AssertProjectionMergeCensus

func AssertProjectionMergeCensus(w io.Writer, floors *ProjectionMergeFloors) bool

AssertProjectionMergeCensus checks the claim the name-arm deletion rests on. It reports whether anything failed.

The ZERO is the claim; the floors and the partition check are what keep the zero from being vacuous. Both are needed, and in this order: a zero over a population nothing reached, and a zero produced by a counter that stopped being wired, are indistinguishable in a printed report.

func AssertUnresolvedResultTypeCensus

func AssertUnresolvedResultTypeCensus(w io.Writer, floors *UnresolvedResultTypeFloors) bool

AssertUnresolvedResultTypeCensus checks the process census against floors.

func ComputeDerivations

func ComputeDerivations(expr expressions.RelationalExpression) *properties.Derivations

ComputeDerivations evaluates the derivations property for a physical plan wrapper expression. Dispatches on the concrete wrapper type, matching Java's DerivationsVisitor per-plan methods.

Exported for use by plan_properties.go and tests.

func CreateScansForMatches

func CreateScansForMatches(
	accesses []Vectored[*SingleMatchedAccess],
	_ PlanContext,
) map[PartialMatch]plans.RecordQueryPlan

CreateScansForMatches converts each SingleMatchedAccess into a physical scan plan by calling MatchCandidate.ToScanPlan with the bound parameter prefix and reverse flag.

Returns a map from PartialMatch to RecordQueryPlan. The map uses the PartialMatch identity (pointer equality) as key.

Ports Java's AbstractDataAccessRule.createScansForMatches.

func CrossProduct

func CrossProduct[T any](lists [][]T) [][]T

CrossProduct computes the Cartesian product of N lists. Each list provides alternatives for one "dimension"; the result is every combination of picking one element from each list.

For input [[a, b], [x, y]], returns [[a, x], [a, y], [b, x], [b, y]]. Empty input returns nil. Any empty inner list makes the product empty.

Mirrors Java's CrossProduct.crossProduct utility.

func DataAccessForMatchPartition

func DataAccessForMatchPartition(
	requestedOrderings []*properties.RequestedOrdering,
	partialMatches []PartialMatch,
	ctx PlanContext,
	intersector IntersectorFunc,
) []expressions.RelationalExpression

DataAccessForMatchPartition orchestrates data access planning from a collection of PartialMatches. This is the main entry point that concrete rules call.

Steps:

  1. Compute maximum-coverage matches (with Pareto filtering)
  2. Create scan plans for each viable match
  3. For single match: wrap as expression with compensation
  4. For multiple matches: try intersections via the provided intersector callback

The intersector is a function parameter so concrete rules can provide their own intersection logic (replaces Java's abstract createIntersectionAndCompensation method).

Ports Java's AbstractDataAccessRule.dataAccessForMatchPartition.

func ExplainPhysicalPlan

func ExplainPhysicalPlan(expr expressions.RelationalExpression) string

ExplainPhysicalPlan returns the Explain() string for a physical-plan expression, or empty string if the expression is not a physical plan.

func ExtractBestPlan

ExtractBestPlan walks the Reference DAG rooted at `ref` and returns a fresh RelationalExpression tree where every reachable Reference is a singleton holding the cost-cheapest member chosen under DefaultStatistics. Children are extracted recursively.

Use case: callers holding an explored Reference (rule-generated alternatives attached) who want to materialise a single best plan for execution / display / serialisation. Without this, the extracted "plan" is the Reference DAG with multiple alternative members still attached; downstream consumers have no easy way to lock in one shape.

Behaviour:

  • Returns nil if `ref` is nil or empty.
  • The returned expression is structurally fresh — its Quantifiers range over freshly-allocated single-member Reference objects. Pointer identity with the input tree's References is NOT preserved.
  • Cycle-safe: a visited set guards against infinite recursion through cyclic Reference DAGs (e.g. recursive CTE expression trees where the RecursiveUnion's quantifiers could form back-edges through the Memo).
  • Switch-on-concrete-type for constructor dispatch — each concrete RelationalExpression type has an arm. Adding a new type requires extending this switch.

Returns an error if any reachable expression is of a type unknown to this extractor — surfacing the missing arm rather than silently dropping or panicking.

See ExtractBestPlanWith for the stats-bound variant.

func ExtractBestPlanFromSelector

ExtractBestPlanFromSelector returns a fresh executable tree where every Reference's physical member comes from `sel` if available; otherwise it cost-selects from physical members only. A selector-named LogicalSort is the one exception: it is accepted only when sort elision immediately resolves it to a physical ordered child.

Use this when the caller has a pre-populated selector (e.g. the cascades.Planner after PlanWithContext). It avoids repeating the OPTIMIZE work that already happened during planning.

Pass nil sel to use the physical-only cost fallback. Use ExtractBestPlanWith when intentionally extracting a still-logical DAG.

func ExtractBestPlanFromSelectorContext

func ExtractBestPlanFromSelectorContext(
	ctx context.Context,
	ref *expressions.Reference,
	sel BestMemberSelector,
	stats properties.StatisticsProvider,
) (expressions.RelationalExpression, error)

ExtractBestPlanFromSelectorContext is the cancelable form of ExtractBestPlanFromSelector. It polls ctx throughout recursive extraction so a canceled planning run does not continue rebuilding a large plan DAG.

func ExtractBestPlanWith

ExtractBestPlanWith is ExtractBestPlan driven by a specific StatisticsProvider. Stats flow into per-Reference best-member selection so different table cardinalities can flip which alternative wins.

Pass nil for stats to use DefaultStatistics (equivalent to ExtractBestPlan).

func FinalizePlan

func FinalizePlan(plan plans.RecordQueryPlan)

FinalizePlan performs the plan-time bake: it builds ONE type repository for the whole plan and stamps every reachable *values.RecordConstructorValue with the message descriptor for its result type.

This is Java's QueryPlan.java:639-643, which builds one TypeRepository per plan from usedTypes().evaluate(relationalExpression) and hands it to the evaluation context for RecordConstructorValue.eval to read back (RecordConstructorValue.java:113-114). Go stamps the descriptor onto the value instead of carrying it on a context, because Go has no uniform evaluation context to carry it on — RFC-204 §4.5.1 records the decision and the reasoning, and TestFrontierContextIsNotAUniformCarrier pins the premise it rests on.

One repository per plan is what makes the COMMON case free; it is not a correctness requirement. Descriptor identity is per-repository, so two constructors of the same record type stamped from different repositories produce messages that are wire-compatible but not identity-equal. Within one plan that never happens, which is why a nested constructor's message can be set straight into its parent's field — Java's deepCopyIfNeeded (RecordConstructorValue.java:165-216) has nothing to reconcile there.

ACROSS plans it does happen, by design: a scalar subquery is planned and baked as its own plan (embedded/scalar_subquery_planning.go calls FinalizePlan on it separately, because it hangs off the cascadesPlan rather than off any child edge of the outer plan), so it carries its own repository. A message built from that repository and set into an outer constructor's field takes the by-number copy in values.rowMessageToProtoValue — the port of deepCopyIfNeeded — and lands correctly. So the guarantee this walk provides is "one repository per plan", and the copy is what covers the rest.

Call it exactly once per plan, on the plan-cache MISS path before PlanCache.Put. The cache returns the SAME plan pointer to every subsequent execution and each page rebuilds its cursor hierarchy from it concurrently, so stamping at execution time would be a data race on values.

A type with no message form (a bare scalar record field, an erased array) yields a *values.ProtoTypeError from MessageDescriptorFor. That is NOT a query failure: the constructor simply stays unstamped and evaluates to its name-keyed map, exactly as it did before the bake existed.

func FireExpressionRule

func FireExpressionRule(rule ExpressionRule, ref *expressions.Reference) ([]expressions.RelationalExpression, error)

FireExpressionRule is a standalone driver for testing ExpressionRules. Matches the rule's pattern against every member of `ref` and invokes OnMatch for each successful match. Yields are inserted into `ref` via the ExpressionRuleCall's Reference; the returned slice is the rule's intent (Yielded()).

Production rule driving lives in the planner's task stack (unified_tasks.go); this helper is the testable entry point — same pattern as FireRule for predicate/value rules.

func FireExpressionRuleWithMemo

func FireExpressionRuleWithMemo(rule ExpressionRule, ref *expressions.Reference, ctx PlanContext, memo *Memo) ([]expressions.RelationalExpression, error)

FireExpressionRuleWithMemo is like FireExpressionRule but passes a PlanContext and Memo to the rule call, enabling cross-Reference memoization when running inside the Planner.

func FireImplementationRule

func FireImplementationRule(rule ImplementationRule, ref *expressions.Reference, constraints ...*ConstraintMap) ([]expressions.RelationalExpression, error)

FireImplementationRule runs an ImplementationRule against a Reference, matching each member and collecting yielded expressions. Returns the yielded expressions (also inserted into ref.Members).

func FireImplementationRuleWithContext

func FireImplementationRuleWithContext(rule ImplementationRule, ref *expressions.Reference, ctx PlanContext, memo *Memo, constraints ...*ConstraintMap) ([]expressions.RelationalExpression, error)

FireImplementationRuleWithContext is like FireImplementationRule but threads a PlanContext through to the rule call. The planner uses this to provide PK / match-candidate info to rules that need it.

func FireRule

func FireRule(rule CascadesRule, in any) ([]any, error)

FireRule drives a predicate/value rule once against `in`: run `rule.Matcher()`, and for every successful match invoke `rule.OnMatch`. Returns the aggregate list of yielded replacements across all matches. Used by rule unit tests; production predicate simplification runs through Simplify's fixpoint loop (simplifier.go), and planner rules through the task stack (unified_tasks.go).

func FormatMergeSlotTypingCensus

func FormatMergeSlotTypingCensus() string

FormatMergeSlotTypingCensus renders the tally for a harness report.

func FormatProjectionMergeCensus

func FormatProjectionMergeCensus() string

FormatProjectionMergeCensus renders the counts for a corpus report.

func FormatUnresolvedResultTypeCensus

func FormatUnresolvedResultTypeCensus() string

FormatUnresolvedResultTypeCensus renders the process census for a harness to log.

func Get

func Get[T any](cm *ConstraintMap, ref *expressions.Reference, key *PlannerConstraint[T]) (T, bool)

Get retrieves the constraint value for a Reference + key combination. The Reference is canonicalized: a constraint pushed on a since-merged alias must stay visible to a reader holding the survivor (and vice versa) — the map's identity is the GROUP, not the pointer.

func GetPhysicalMultiIntersectionPlan

GetPhysicalMultiIntersectionPlan returns the plan if expr is a multi-intersection, nil otherwise. Since RFC-184 W2 the memo holds the bare plan, so this is a bare type assertion.

func IndexPredicateProtoIsTautology

func IndexPredicateProtoIsTautology(p *gen.Predicate) bool

IndexPredicateProtoIsTautology reports whether a stored index predicate provably rejects no record — i.e. whether the index it guards holds an entry for every record. Normalize the way Java's constructors do, then apply Java's narrow tautology test to the result.

func InitialOf

InitialOf, FinalOf, and FinalOfAtStage are the checked root factories. They return no Reference until exact admission and the complete singleton commit succeed.

func IsPhysicalAggregateIndex

func IsPhysicalAggregateIndex(expr expressions.RelationalExpression) bool

IsPhysicalAggregateIndex reports whether the expression is an aggregate index scan. Since RFC-184 W2 the memo holds *plans.RecordQueryAggregateIndexPlan directly (no physicalAggregateIndexWrapper), so this is a bare type check.

func IsPhysicalDelete

func IsPhysicalDelete(expr expressions.RelationalExpression) bool

IsPhysicalDelete reports whether the given RelationalExpression is a DELETE plan. Since RFC-184 W2 the memo holds *plans.RecordQueryDeletePlan directly (no physicalDeleteWrapper), so this is a bare type check.

func IsPhysicalFetchFromPartialRecord

func IsPhysicalFetchFromPartialRecord(expr expressions.RelationalExpression) bool

IsPhysicalFetchFromPartialRecord reports whether the given RelationalExpression is a physical fetch. Since RFC-184 W2 the memo holds *plans.RecordQueryFetchFromPartialRecordPlan directly (no physicalFetchFromPartialRecordWrapper), so this is a bare type check on the plan.

func IsPhysicalFilter

func IsPhysicalFilter(expr expressions.RelationalExpression) bool

IsPhysicalFilter reports whether the given RelationalExpression is a physical predicates filter. Since RFC-184 W2 the memo holds *plans.RecordQueryPredicatesFilterPlan directly (no physicalPredicatesFilterWrapper), so this is a bare type check.

func IsPhysicalFlatMap

func IsPhysicalFlatMap(expr expressions.RelationalExpression) bool

IsPhysicalFlatMap reports whether the given RelationalExpression is a physical correlated FlatMap. Since RFC-184 W2 the memo holds *plans.RecordQueryFlatMapPlan directly (no physicalFlatMapWrapper), so this is a bare type check.

func IsPhysicalInJoin

func IsPhysicalInJoin(expr expressions.RelationalExpression) bool

IsPhysicalInJoin reports whether the given expression is an InJoin. Since RFC-184 W2 the memo holds *plans.RecordQueryInJoinPlan directly (no physicalInJoinWrapper), so this is a bare type check.

func IsPhysicalIndexScan

func IsPhysicalIndexScan(expr expressions.RelationalExpression) bool

IsPhysicalIndexScan reports whether the given RelationalExpression is an index scan. Since RFC-184 W2 the memo holds the plan directly (no physicalIndexScanWrapper), so this is a type check on the plan.

A COVERING index scan answers TRUE. It is an index scan — it reads the same physical index range and differs only in the row it reconstructs — and since RFC-220 it is the shape the memo actually holds for an index-backed access. A predicate named "is an index scan" that answered FALSE for the only index scan in the memo would be a trap for every caller.

func IsPhysicalInsert

func IsPhysicalInsert(expr expressions.RelationalExpression) bool

IsPhysicalInsert reports whether the given RelationalExpression is an INSERT plan. Since RFC-184 W2 the memo holds *plans.RecordQueryInsertPlan directly (no physicalInsertWrapper), so this is a bare type check.

func IsPhysicalIntersection

func IsPhysicalIntersection(expr expressions.RelationalExpression) bool

IsPhysicalIntersection reports whether the given RelationalExpression is an intersection. Since RFC-184 W2 the memo holds *plans.RecordQueryIntersectionPlan directly (no physicalIntersectionWrapper), so this is a bare type check.

func IsPhysicalLimit

func IsPhysicalLimit(expr expressions.RelationalExpression) bool

IsPhysicalLimit reports whether the given RelationalExpression is a physical LIMIT. Since RFC-184 W2 the memo holds *plans.RecordQueryLimitPlan directly (no physicalLimitWrapper), so this is a bare type check on the plan.

func IsPhysicalMap

func IsPhysicalMap(expr expressions.RelationalExpression) bool

IsPhysicalMap reports whether the given expression is a physical map. Since RFC-184 W2 the memo holds *plans.RecordQueryMapPlan directly (no physicalMapWrapper), so this is a bare type check on the plan.

func IsPhysicalMultiIntersection

func IsPhysicalMultiIntersection(expr expressions.RelationalExpression) bool

IsPhysicalMultiIntersection reports whether the given RelationalExpression is a multi-intersection. Since RFC-184 W2 the memo holds *plans.RecordQueryMultiIntersectionOnValuesPlan directly (no physicalMultiIntersectionWrapper), so this is a bare type check.

func IsPhysicalNestedLoopJoin

func IsPhysicalNestedLoopJoin(expr expressions.RelationalExpression) bool

IsPhysicalNestedLoopJoin reports whether the given RelationalExpression is a physical nested-loop join. Since RFC-184 W2 the memo holds *plans.RecordQueryNestedLoopJoinPlan directly (no physicalNestedLoopJoinWrapper), so this is a bare type check.

func IsPhysicalPredicatesFilter

func IsPhysicalPredicatesFilter(expr expressions.RelationalExpression) bool

IsPhysicalPredicatesFilter reports whether the given expression is a physical predicates filter. Since RFC-184 W2 the memo holds *plans.RecordQueryPredicatesFilterPlan directly (no physicalPredicatesFilterWrapper), so this is a bare type check.

func IsPhysicalRecursiveDfsJoin

func IsPhysicalRecursiveDfsJoin(expr expressions.RelationalExpression) bool

IsPhysicalRecursiveDfsJoin reports whether the given RelationalExpression is a recursive DFS join. Since RFC-184 W2 the memo holds *plans.RecordQueryRecursiveDfsJoinPlan directly (no physicalRecursiveDfsJoinWrapper), so this is a bare type check.

func IsPhysicalRecursiveLevelUnion

func IsPhysicalRecursiveLevelUnion(expr expressions.RelationalExpression) bool

IsPhysicalRecursiveLevelUnion reports whether the given RelationalExpression is a recursive level union. Since RFC-184 W2 the memo holds *plans.RecordQueryRecursiveLevelUnionPlan directly (no physicalRecursiveLevelUnionWrapper), so this is a bare type check.

func IsPhysicalStreamingAgg

func IsPhysicalStreamingAgg(expr expressions.RelationalExpression) bool

IsPhysicalStreamingAgg reports whether the given RelationalExpression is a physical streaming aggregation. Since RFC-184 W2 the memo holds *plans.RecordQueryStreamingAggregationPlan directly (no physicalStreamingAggWrapper), so this is a bare type check.

func IsPhysicalUpdate

func IsPhysicalUpdate(expr expressions.RelationalExpression) bool

IsPhysicalUpdate reports whether the given RelationalExpression is an UPDATE plan. Since RFC-184 W2 the memo holds *plans.RecordQueryUpdatePlan directly (no physicalUpdateWrapper), so this is a bare type check.

func MergeSlotTypeDisagreements

func MergeSlotTypeDisagreements() (int64, []string)

MergeSlotTypeDisagreements returns how many positional merges declined because a leg quantifier's reference members disagreed on their row type, plus the distinct witnesses retained (capped). A nonzero count over any corpus is a memo defect: either a rule inserted an expression flowing a different row into an existing equivalence class, or a leg's row type is being derived two ways.

func NestPullUp

func NestPullUp(
	pm PartialMatch,
	pullUp *PullUp,
	candidateAlias values.CorrelationIdentifier,
) (rootOfMatchPullUp *PullUp, currentPullUp *PullUp)

NestPullUp walks through the candidate reference chain to build a nested PullUp chain. For each level, it visits the candidate expression to determine the pull-through value. If the match info is adjusted (wrapping another), it descends through the adjustment chain.

Returns (rootOfMatchPullUp, currentPullUp). currentPullUp is the innermost level, used to translate predicates and to nest child matches. rootOfMatchPullUp is the level this nesting introduced on top of the incoming chain — the one result compensation pulls the query result value through. It is nil when the incoming pull-up is itself a match level (this nesting continues someone else's match rather than rooting a new one), which is Java's signal that no result compensation is owed here.

currentPullUp is nil when the chain cannot be built faithfully: a missing candidate reference, a reference that does not hold exactly one non-nil member, or an adjusted level whose candidate does not have exactly one quantifier. Java asserts those (Reference.get, which throws unless the reference holds exactly one member, and Verify.verify on the quantifier count) and crashes; Go fails closed instead so the caller degrades the match to an impossible compensation. Both refuse to compensate off a guessed chain — silently taking members[0] of a multi-member reference builds a pull-up for an expression the match was never proved against, and every value pulled through it is then wrong.

Ports Java's PartialMatch.nestPullUp.

func NewPlanningCostModelLess

func NewPlanningCostModelLess(stats properties.StatisticsProvider) func(a, b expressions.RelationalExpression) bool

NewPlanningCostModelLess returns a stats-aware cost model comparator with no PlanContext (index metadata for cardinality/unmatched-field criteria is resolved conservatively). Prefer NewPlanningCostModelLessWithContext.

func NewPlanningCostModelLessWithContext

func NewPlanningCostModelLessWithContext(stats properties.StatisticsProvider, ctx PlanContext) func(a, b expressions.RelationalExpression) bool

NewPlanningCostModelLessWithContext returns a stats-aware cost model comparator. The returned function uses real record counts (via stats) for cardinality estimation and resolves index/primary-key metadata via ctx so the criterion-#2 (provable max cardinality) and criterion-#12 (unmatched primary-scan/index key fields) properties are computed faithfully from the CONCRETE plan tree (RFC-069). Pass nil stats for default (LeafScanCardinality); pass nil ctx to resolve index metadata conservatively (treat indexes as non-unique).

func NormalizeDNF

func NormalizeDNF(pred predicates.QueryPredicate, sizeLimit int) (predicates.QueryPredicate, bool)

NormalizeDNF converts a predicate to disjunctive normal form (DNF). Returns (result, true) if a transformation was applied, or (original, false) if the predicate is already in DNF or the normalised form would exceed sizeLimit.

DNF: the outer connective is OR, each child is either a leaf or an AND of leaves. The transformation distributes AND over OR:

A AND (B OR C) -> (A AND B) OR (A AND C)

Mirrors Java's BooleanPredicateNormalizer with Mode.DNF.

func NormalizeDNFWithoutSimplification

func NormalizeDNFWithoutSimplification(pred predicates.QueryPredicate, sizeLimit int) (predicates.QueryPredicate, bool)

NormalizeDNFWithoutSimplification is the Go port of BooleanPredicateNormalizer.getDefaultInstanceForDnf().normalize(pred, false) (BooleanPredicateNormalizer.java:227-230): pure distribution into disjunctive normal form with NOT pushed down over AND/OR via De Morgan (toNormalized's negate flag, :370-384), but — unlike NormalizeDNF above, which matches normalizeAndSimplify (:209-215) — WITHOUT absorption and WITHOUT dedup. The distinction is wire-visible for the index-predicate producer (MaterializedViewIndexGenerator.java:675 calls normalize, not normalizeAndSimplify): an absorbed clause would change the stored RecordMetaDataProto.Predicate bytes relative to Java.

Returns (pred, false) when the predicate is already in normal form (isInNormalForm, :255-293) or the normalized size would exceed sizeLimit (shouldNormalize with failIfTooLarge=false, :295-303) — Java's Optional.empty(), which the caller resolves with orElse(conjunction).

func NormalizeIndexPredicateProto

func NormalizeIndexPredicateProto(p *gen.Predicate) *gen.Predicate

THE INVARIANT, for this function and every caller of it: normalization may only ERASE a predicate it can PROVE accepts every record. Anything it cannot prove — an arm it does not understand, a malformed message, a child it cannot compile — must stay VISIBLE to the sparseness gates, because those gates read absence as "this index holds everything". Erasing on doubt turns a partial index into a full one and a scan of it into wrong rows; keeping a predicate that was in fact harmless costs at worst a declined candidate.

NormalizeIndexPredicateProto applies Java's predicate CONSTRUCTION rules to a stored index predicate, returning the equivalent predicate Java would have built. It is the single authority on that question: the planner (deciding whether a candidate carries a predicate to account for) and the executor (deciding whether an index is complete enough to scan) both classify through it, so they cannot answer differently about the same stored bytes.

Java never classifies a conjunction of tautologies, because it never constructs one. IndexPredicate.AndPredicate.toPredicate delegates to AndPredicate.and (IndexPredicate.java:344-345), and that constructor drops every tautological conjunct, returns ConstantPredicate.TRUE when none remain, and unwraps a lone survivor (AndPredicate.java:188-206). `TRUE AND TRUE` has therefore already become the constant before anything asks whether it filters.

OR IS NOT THE DUAL, and deliberately so. OrPredicate.or delegates to OrPredicate.of (OrPredicate.java:417-445), which only collapses a single-element disjunction — it does no tautology folding — and OrPredicate never overrides QueryPredicate.isTautology (default false, QueryPredicate.java:311). So `TRUE OR x` is NOT a tautology to Java, and folding it here would make Go's completeness proof WIDER than Java's: Go would scan an index Java treats as filtering. The asymmetry is Java's, not an oversight to be tidied up.

A ROW-NUMBER WINDOW arm is likewise never folded, and that is a DIVERGENCE from Java's conversion, taken deliberately. Java's RowNumberWindowPredicate.toPredicate returns ConstantPredicate.TRUE (IndexPredicate.java:770-772) — but that states only that the constraint is not expressible as a QueryPredicate over a record, NOT that it filters nothing. It filters maximally: `QualifyRowNumber(score, DESC) <= 100` "keeps the 100 records with the highest score values in the index" (IndexPredicate.java:608-619). Folding it to the constant here would classify a top-N index as COMPLETE and let a scan serve it as the whole table.

Leaving it unfolded keeps it VISIBLE to the sparseness gates, and that alone turned out not to be enough: the flat expansion converts the predicate and omits a tautological result, so a row-window arm that reached indexPredicateToQueryPredicate was mapped to TRUE and dropped, and the candidate matched as full whether the arm arrived bare or wrapped in an AND. The conversion therefore REFUSES the arm outright, which excludes the candidate.

That refusal is LOAD-BEARING rather than belt-and-braces: a store can carry such an index, because the record layer maintains one — a vector index whose stored predicate declares the window is decorated by slidingWindowIndexMaintainer, which keeps only the qualifying records in the wrapped HNSW graph. The arm's compiled per-record evaluator answers `true` for every record (Java's shouldIndexThisRecord), and it is exactly that answer which must never reach the completeness question here.

The result is always a deep clone; the input is never mutated and the two trees share no nodes.

func OptionalRewritingRuleNames

func OptionalRewritingRuleNames() []string

OptionalRewritingRuleNames returns the names of the rules Java's `RecordQueryPlannerConfiguration.Builder.disableRewritingRules()` turns off — `RewritingRuleSet.OPTIONAL_RULES`, i.e. every rule of the REWRITING rule set EXCEPT `FinalizeExpressionsRule`, which stays on because it is what stamps an expression FINAL and planning cannot proceed without it.

Java's set is {QueryPredicateSimplificationRule, PredicatePushDownRule, DecorrelateValuesRule, RewriteOuterJoinRule, SelectMergeRule}; all five exist in Go under the same names, so the disabled set is name-identical. SelectMergeRule is in Go's DefaultExpressionRules rather than RewritingRules (Go's REWRITING phase runs the default expression rules), so it is named explicitly instead of being derived from RewritingRules().

The names are the Java simple-class-name spelling, which is exactly what Planner.DisabledRules is keyed by.

func OrderFunctionDirection

func OrderFunctionDirection(name string) (values.OrderedBytesDirection, bool)

OrderFunctionDirection maps an order-function name to its TupleOrdering direction; ok=false for any other function name.

EXACT match, not case-folded. Java never classifies by name at all — it holds the direction as a field on OrderFunctionKeyExpression and dispatches on the type — and it registers the four builders under names already lowercased (OrderFunctionKeyExpressionFactory: `FUNCTION_NAME_PREFIX + direction.name().toLowerCase(Locale.ROOT)`), so no upper-case spelling can name a built-in order function in either engine.

Folding case here was not a harmless convenience. RegisterFunction is public API over a CASE-SENSITIVE map (key_expression.go), so an application may register its own evaluator under `ORDER_ASC_NULLS_FIRST`; the record layer would then encode that column with the application's function while the planner classified it as the built-in tuple-order encoding, deriving ordered ranges — or eliminating a sort — from bytes nothing ever wrote in that order. Exact match keeps this consistent with isOrderFunctionName (order_function_key_expression.go), which never folded.

func OrderingAliases

func OrderingAliases(
	groupingAliases []values.CorrelationIdentifier,
	scoreAlias values.CorrelationIdentifier,
	primaryKeyAliases []values.CorrelationIdentifier,
) []values.CorrelationIdentifier

OrderingAliases is a package-level helper that computes the ordering alias list for a windowed index: grouping + [score] + primaryKey. Ports Java's WindowedIndexScanMatchCandidate.orderingAliases().

func PlanningCostModelLess

func PlanningCostModelLess(a, b expressions.RelationalExpression) bool

PlanningCostModelLess is the Java-aligned multi-criteria plan comparator, with the documented Go-specific criteria called out below.

Returns true if a is strictly preferred over b. The comparison uses these ordered tie-breaking criteria. Parenthetical numbers are the stable Java-aligned labels used by focused tests and design docs; Go extensions do not renumber those labels:

  • Physical plan beats non-physical (#1)
  • Max cardinality of all data accesses, lower wins (#2)
  • Fewer normalized residual predicates (#3)
  • Fewer data access operators: scan + index + covering (#4)
  • Recursive CTE tie-breaker: DFS beats level-based (#5)
  • Join-order cost (#15, broadened and hoisted in Go)
  • IN-plan penalty when IN-values are not SARGs (#6)
  • Primary scan vs index-scan-with-fetch preference (#7)
  • In-memory sort count, fewer wins (Go sort-elimination analogue)
  • Type filter count, fewer wins (#8)
  • Type filter depth, deeper wins (#9)
  • Index fetch metrics, fewer non-covering scans/fetches win (#10)
  • Distinct depth, deeper wins (#11)
  • Unmatched primary-scan/index key field count, fewer wins (#12)
  • IN-join source count, more wins (#13)
  • MAP + PredicatesFilter count, fewer wins (#14)
  • Nested-loop-join predicate count, more wins (Go extension)
  • ON EMPTY NULL operation count, fewer wins (#16)
  • Scalar cost fallback (Go statistics extension)
  • Plan hash deterministic tie-break (#17)

func PredicatePushesBelowGroupBy

func PredicatePushesBelowGroupBy(p predicates.QueryPredicate, groupingKeys []values.Value) bool

PredicatePushesBelowGroupBy reports whether one predicate over a GroupBy's output can be evaluated BEFORE the aggregation — the decision this rule makes per predicate, exported because the SQL translator has to make the SAME call upstream: a HAVING reference that will be pushed below must keep its pre-aggregate binding, and one that will not must be rebased onto the aggregate's output row. Two answers to one question is a wrong-row read on whichever side loses.

It was TWO implementations, and they had already drifted in three ways that a shared decider removes: the translator's copy matched a group key by BARE LEAF NAME (so a nested `addr.city` key answered to a top-level `city`), it never checked the COMPARAND (so `key > SUM(v)` — which this rule refuses to push — was told it would be pushed), and it did not require every grouping key to have an establishable identity.

func RegisteredRuleNames

func RegisteredRuleNames() []string

RegisteredRuleNames returns a sorted list of registered rule names. Useful for tests that want to iterate the registry deterministically.

func ResetMergeSlotTypingCensus

func ResetMergeSlotTypingCensus()

ResetMergeSlotTypingCensus zeroes the tally.

func ResetOrderingComparisonCensus

func ResetOrderingComparisonCensus()

ResetOrderingComparisonCensus zeroes every site's counts.

It exists because these counters are PACKAGE-scoped, and this package has learned what that costs: ReachabilityCollector's header records a corpus tally that read 3x its true value because sibling tests planned the same corpus in parallel. The same hazard applies here, with one difference that makes the package scope tolerable — the assertion built on these counts is a ZERO, and a zero over a sum of non-negative terms is exact no matter how many extra corpus passes contribute to it. A concurrent pass can only make the assertion FAIL, never falsely pass.

The absolute TOTALS have no such protection: they are a lower bound unless the census ran alone. Reset before a pass and run that pass with -run isolation if the totals themselves are the artifact you need.

func ResetProjectionMergeCensus

func ResetProjectionMergeCensus()

ResetProjectionMergeCensus zeroes the counts.

The counters are PACKAGE-scoped, so a sibling test planning in parallel adds to them. The assertion built on them is a ZERO, and a zero over a sum of non-negative terms is exact however many extra passes contribute — a concurrent pass can only make it FAIL, never falsely pass. The absolute totals have no such protection and are a lower bound unless the pass ran alone.

func ResetUnresolvedResultTypeCensus

func ResetUnresolvedResultTypeCensus()

ResetUnresolvedResultTypeCensus clears the process counters. Present so a test can drive the REAL recorder — recordResultTypeRead, by name — rather than a local copy of its body, which is the mutation a copy would survive.

func ResolveComparisonDirection

func ResolveComparisonDirection(parts []properties.ProvidedOrderingPart) bool

ResolveComparisonDirection examines the comparison ordering parts and determines if the comparison is reverse (all descending). Returns true if all directional parts are descending. Mirrors Java's RecordQuerySetPlan.resolveComparisonDirection.

func RewritingCostModelLess

func RewritingCostModelLess(a, b expressions.RelationalExpression) bool

RewritingCostModelLess is the cost model for the REWRITING phase. It ports the tail of Java's RewritingCostModel.compare():

  1. Fewer SelectExpressions
  2. Fewer TableFunctionExpressions
  3. Fewer normalized residual predicate conjuncts (CNF full-size)
  4. More predicates at deeper levels (push predicates down)
  5. Semantic hash tiebreak

DELIBERATE OMISSION — Java's FIRST criterion, outerJoinCount (penalize any surviving OuterJoinExpression), is NOT ported, and must not be. In Java it is a CORRECTNESS GUARD, not a heuristic: Java's OuterJoinExpression is a logical-only node with NO physical operator and exactly one consumer (RewriteOuterJoinRule), so it MUST be rewritten before planning. Java's single-final-expression prune keeps one survivor per group; without outerJoinCount the un-rewritten OuterJoinExpression (0 selects) would beat the rewritten form (2 selects) on the selectCount tie-break, survive the prune, and leave the planning phase with an UNIMPLEMENTABLE node — the query would fail to plan. outerJoinCount forces the implementable rewritten form to survive.

Go has no such correctness problem, because Go's outer join IS directly implementable: an outer-join SelectExpression is planned by ImplementNestedLoopJoinRule as a MATERIALIZED RecordQueryNestedLoopJoinPlan (RFC-152) — a read-side extension Java lacks (Java has only the correlated FlatMap re-scan). Go deliberately keeps the un-rewritten outer-join select as the REWRITING prune survivor (it wins on selectCount, 1<2) precisely so PLANNING can derive BOTH the materialized NLJ (scan the inner once) AND the correlated FlatMap (re-scan) and cost-choose. Porting outerJoinCount would force the rewritten form to win the prune, discard the outer-join select, and thereby SUPPRESS the materialized-NLJ alternative — a regression, not a fix (pinned by TestFDB_ArrayUnnestOrdinality, which asserts the materialized NLJ box, and by TestRewritingCostModel_KeepsUnrewrittenOuterJoin). RFC-186: every tier derives through DESIGNATED child finals (the virtual prune, designated_final.go) — a function of a deterministically-chosen candidate tree, never of memo-population history. The pre-RFC-186 tiers summed over ALL memo members (exploratory included), so two equivalent candidates scored differently by how many rewrites their child groups happened to accumulate — and tier 3's physical-only descent counted nothing at all on the all-logical REWRITING memo. This package-level function mints a fresh designation scope per call (identical semantics to the planner-owned scope, merely uncached); the planner's REWRITING cost model uses its own scope so OptimizeGroup winners and designations come from the SAME comparator (coherence, RFC-186 instrument).

func Set

func Set[T any](cm *ConstraintMap, ref *expressions.Reference, key *PlannerConstraint[T], value T) bool

Set stores a constraint value for a Reference + key combination (the Reference canonicalized — see Get). Set pushes a constraint with Java pushProperty semantics (RFC-181 WS-P stage (b) first commit): the per-key LATTICE COMBINE decides — an absent key stores the push; a present key stores the combined value when the lattice grew and SUBSUMES the push otherwise (no store, no tick — Java's empty Optional). Both stores see the same combined value: the planner-global map (read-authoritative) and the per-Reference epoch mirror (which ticks only on real change, so an unchanged re-Set per rule re-fire can never hold a group unconverged once epochs drive convergence). The former plain overwrite ALSO silently clobbered a shared child's accumulated referenced fields when two parents pushed different sets — the union combine is the Java-faithful repair. The returned verdict reports whether the lattice GREW — Java's pushProperty Optional presence. Callers gate re-exploration scheduling on it (a subsumed push schedules nothing).

func SetOrderingComparisonCensusEnabled

func SetOrderingComparisonCensusEnabled(on bool)

SetOrderingComparisonCensusEnabled turns the census on or off. It is OFF in every production path and in every test but the corpus census, which needs the counts and is the only reader; when off, each comparison pays one relaxed atomic load.

func Simplify

Simplify iterates the rules on `pred` until no rule produces a rewrite, then returns the final form. Each iteration applies every rule in order; if ANY rule yields, the result replaces `pred` and the loop restarts from the top (fixpoint convergence). Descends into child predicates after the top-level stabilises.

Termination invariants for the rule sets this driver runs:

  • DefaultSimplifyRules — each rule strictly reduces the tree (folds a constant or drops an identity / absorbed child), and there's a finite number of constants / identities to collapse.
  • NormalizationRules — adds DeMorganRule, which strictly INCREASES the node count (NOT distributed across N children adds N-1 NOTs). DeMorganRule terminates via NOT-depth monotone decrease: each application moves every NOT one level closer to leaves, leaves are finitely deep, leaves eventually hit NotComparisonRewriteRule and disappear (or stop matching).

Not safe against cyclic-rewrite rule sets — real Cascades uses a memo to detect cycles. The rule sets this driver runs are termination-proven per above so no cycle is possible.

func UnresolvedResultTypeCensus

func UnresolvedResultTypeCensus() (map[string]int, map[string]int)

UnresolvedResultTypeCensus reports a SNAPSHOT of the counters as two maps, resolved and unresolved, keyed by deciding site.

func ValidatePlanInvariants

func ValidatePlanInvariants(plan plans.RecordQueryPlan) error

ValidatePlanInvariants checks RFC-164 WS-2 structural invariants on a fully extracted physical plan tree. It is a cheap, always-on backstop — run in the no-FDB plan harness, the production generator, and a fuzz target — that makes a whole class of malformed plans un-shippable rather than hunting them one PR at a time.

It has TWO detectors for a relink that dropped a child:

  • Empty-children: a non-leaf plan with zero children. A unary operator whose inner was left nil (the IN-LIMIT bug) has its nil masked by GetChildren (which returns empty for a nil inner), so it masquerades as a leaf. Comparing against the fixed set of genuinely-childless plan types unmasks it.
  • Nil-in-slice: a fixed-arity operator (NLJ, FlatMap, recursive joins/unions) returns a length-N slice whose dropped child is a nil ELEMENT rather than a shorter slice; the per-child nil check below catches that.

It walks the PLAN tree, not the expression tree: the malformation lives in the eagerly-materialized plan SNAPSHOT, while the live expression member (the wrapper's quantifier) still points at a healthy reference — so an expression-tree walk traverses the healthy edge and never reaches the break.

Types

type AdjustedBuilder

type AdjustedBuilder struct {
	// contains filtered or unexported fields
}

AdjustedBuilder builds an AdjustedMatchInfo from an underlying MatchInfo, allowing selective override of ordering parts, max match map, and group-by mappings.

Ports Java's MatchInfo.AdjustedBuilder.

func NewAdjustedBuilder

func NewAdjustedBuilder(mi MatchInfo) *AdjustedBuilder

NewAdjustedBuilder creates an AdjustedBuilder pre-seeded with the given MatchInfo's current values. Equivalent to Java's MatchInfo.adjustedBuilder() default method.

func (*AdjustedBuilder) Build

func (b *AdjustedBuilder) Build() *AdjustedMatchInfo

Build constructs the AdjustedMatchInfo.

func (*AdjustedBuilder) GetGroupByMappings

func (b *AdjustedBuilder) GetGroupByMappings() *GroupByMappings

GetGroupByMappings returns the builder's current group-by mappings.

func (*AdjustedBuilder) GetMatchedOrderingParts

func (b *AdjustedBuilder) GetMatchedOrderingParts() []*MatchedOrderingPart

GetMatchedOrderingParts returns the builder's current ordering parts.

func (*AdjustedBuilder) GetMaxMatchMap

func (b *AdjustedBuilder) GetMaxMatchMap() *MaxMatchMap

GetMaxMatchMap returns the builder's current max match map.

func (*AdjustedBuilder) SetGroupByMappings

func (b *AdjustedBuilder) SetGroupByMappings(g *GroupByMappings) *AdjustedBuilder

SetGroupByMappings overrides the group-by mappings.

func (*AdjustedBuilder) SetMatchedOrderingParts

func (b *AdjustedBuilder) SetMatchedOrderingParts(parts []*MatchedOrderingPart) *AdjustedBuilder

SetMatchedOrderingParts overrides the ordering parts.

func (*AdjustedBuilder) SetMaxMatchMap

func (b *AdjustedBuilder) SetMaxMatchMap(m *MaxMatchMap) *AdjustedBuilder

SetMaxMatchMap overrides the max match map.

type AdjustedMatchInfo

type AdjustedMatchInfo struct {
	// contains filtered or unexported fields
}

AdjustedMatchInfo wraps an underlying MatchInfo with adjusted ordering parts, max match map, and/or group-by mappings. Created by the AdjustMatchRule when an existing match is refined by walking up the Traversal on the candidate side.

Ports Java's MatchInfo.AdjustedMatchInfo.

func NewAdjustedMatchInfo

func NewAdjustedMatchInfo(
	underlying MatchInfo,
	matchedOrderingParts []*MatchedOrderingPart,
	maxMatchMap *MaxMatchMap,
	groupByMappings *GroupByMappings,
) *AdjustedMatchInfo

NewAdjustedMatchInfo constructs an AdjustedMatchInfo.

func (*AdjustedMatchInfo) GetGroupByMappings

func (a *AdjustedMatchInfo) GetGroupByMappings() *GroupByMappings

GetGroupByMappings returns the adjusted group-by mappings.

func (*AdjustedMatchInfo) GetMatchedOrderingParts

func (a *AdjustedMatchInfo) GetMatchedOrderingParts() []*MatchedOrderingPart

GetMatchedOrderingParts returns the adjusted ordering parts.

func (*AdjustedMatchInfo) GetMaxMatchMap

func (a *AdjustedMatchInfo) GetMaxMatchMap() *MaxMatchMap

GetMaxMatchMap returns the adjusted max match map.

func (*AdjustedMatchInfo) GetRegularMatchInfo

func (a *AdjustedMatchInfo) GetRegularMatchInfo() *RegularMatchInfo

GetRegularMatchInfo delegates to the underlying MatchInfo.

func (*AdjustedMatchInfo) GetUnderlying

func (a *AdjustedMatchInfo) GetUnderlying() MatchInfo

GetUnderlying returns the wrapped MatchInfo.

func (*AdjustedMatchInfo) IsAdjusted

func (a *AdjustedMatchInfo) IsAdjusted() bool

IsAdjusted returns true -- AdjustedMatchInfo is always adjusted.

func (*AdjustedMatchInfo) IsRegular

func (a *AdjustedMatchInfo) IsRegular() bool

IsRegular returns false -- AdjustedMatchInfo is not regular.

type AggregateDataAccessRule

type AggregateDataAccessRule struct {
	// contains filtered or unexported fields
}

AggregateDataAccessRule matches a GroupByExpression against aggregate index candidates (SUM, COUNT, etc.). When a match is found, the rule directly produces an index scan that reads pre-computed aggregates from the aggregate index — no runtime aggregation needed.

GroupBy(keys=[k1], aggs=[SUM(col)], inner=Scan)
  → IndexScan(aggregate_index)   [when aggregate index matches]

For single-aggregate queries, one AggregateIndexMatchCandidate covers the entire GroupBy and produces a direct index scan.

For multi-aggregate queries (e.g. SUM(a), COUNT(*)), each aggregate is served by a separate aggregate index. The rule intersects them via RecordQueryMultiIntersectionOnValuesPlan: all streams are ordered by the same grouping columns (comparison key), and the result row picks grouping values from any stream (they're identical) plus each aggregate from its respective stream.

Mirrors Java's AggregateDataAccessRule, including the structure of createIntersectionAndCompensation().

The compensation-projection ELISION is a Go-only read-side extension, not a mirrored behaviour, and the header used to imply otherwise. Java's equivalent gate cannot fire for a grouped aggregate: AggregateIndexExpansionVisitor publishes Column.unnamedOf(...) — a flat unnamed (_0.._n, agg) row whose getAvailableFields() is NO_FIELDS — so Java always compensates, which is why nearly every AISCAN in its yaml-tests is followed by a MAP. Go's aggregate-index leaf publishes NAMED columns instead, a pre-existing divergence, and that is what makes the elision expressible here at all. Allowed as an extension because wire format is untouched and the elision is checked end to end; do not read it as parity.

func NewAggregateDataAccessRule

func NewAggregateDataAccessRule() *AggregateDataAccessRule

func (*AggregateDataAccessRule) Matcher

func (*AggregateDataAccessRule) OnMatch

func (r *AggregateDataAccessRule) OnMatch(call *ExpressionRuleCall)

type AggregateIndexMatchCandidate

type AggregateIndexMatchCandidate struct {
	// contains filtered or unexported fields
}

AggregateIndexMatchCandidate represents a pre-computed aggregate index in FDB (e.g., SUM, COUNT, MAX_EVER_LONG, MIN_EVER_LONG). Such indexes maintain running aggregates grouped by a set of key columns. A query like "SELECT region, SUM(amount) FROM t GROUP BY region" can be answered directly from a SUM index on (region, amount) without scanning any data rows.

Mirrors Java's `com.apple.foundationdb.record.query.plan.cascades.AggregateIndexMatchCandidate`, carrying the surface AggregateDataAccessRule consumes.

func NewAggregateIndexMatchCandidate

func NewAggregateIndexMatchCandidate(
	indexName string,
	recordTypes []string,
	groupCols []string,
	aggFunction expressions.AggregateFunction,
	aggColumn string,
	baseRowType values.Type,
	groupKeyTypes []values.Type,
	physicalGroupingPrefixCount int,
) *AggregateIndexMatchCandidate

NewAggregateIndexMatchCandidate creates a candidate for an aggregate index. groupCols are the grouping key columns; aggFunction + aggColumn describe the pre-computed aggregate; baseRowType is the declared layout the grouping-column names resolve against (values.UnknownType when there is no single one).

groupKeyTypes and physicalGroupingPrefixCount are PRECONDITIONS, not optional enhancements. Callers must supply the authoritative physical grouping-key types; a candidate without them cannot exist. The reason is that an Unknown physical type must DECLINE range eligibility — the binder cannot prove an exact probe against a type it does not know (scan_range_binding.go's validateAuthoritativeScanPhysicalType) — so a candidate built without them silently declines every binding, and a silently-declining candidate is how aggregate intersection dies invisibly. Making them constructor arguments makes that unsound state unconstructible rather than merely discouraged.

physicalGroupingPrefixCount is the number of grouping columns that stay a contiguous leading prefix of the physical BY_GROUP key: groupingCount for an ordinary aggregate, groupingCount-permutedSize for PERMUTED_MIN/MAX. It is clamped to [0, len(groupCols)].

func (*AggregateIndexMatchCandidate) CandidateName

func (c *AggregateIndexMatchCandidate) CandidateName() string

func (*AggregateIndexMatchCandidate) ComputeBoundParameterPrefixMap

func (*AggregateIndexMatchCandidate) CountsRows

func (c *AggregateIndexMatchCandidate) CountsRows() bool

CountsRows reports whether the index's stored value is the group's row count.

func (*AggregateIndexMatchCandidate) GetAggColumn

func (c *AggregateIndexMatchCandidate) GetAggColumn() string

func (*AggregateIndexMatchCandidate) GetAggFunction

func (*AggregateIndexMatchCandidate) GetBaseRowType

func (c *AggregateIndexMatchCandidate) GetBaseRowType() values.Type

GetBaseRowType returns the declared layout the grouping-column names resolve against, or values.UnknownType when the index has no single one.

func (*AggregateIndexMatchCandidate) GetBaseType

func (c *AggregateIndexMatchCandidate) GetBaseType() values.Type

GetBaseType exposes the exact candidate row through the common MatchCandidate contract consumed by index expansion. Aggregate candidates historically used only the more specific GetBaseRowType spelling, which made the exact-type gate silently exclude every aggregate traversal.

func (*AggregateIndexMatchCandidate) GetColumnNames

func (c *AggregateIndexMatchCandidate) GetColumnNames() []string

func (*AggregateIndexMatchCandidate) GetKeyComponentTypes

func (c *AggregateIndexMatchCandidate) GetKeyComponentTypes() []values.Type

GetKeyComponentTypes returns logical grouping-key types. The scan plan aligns the leading entries to the comparisons it actually carries.

func (*AggregateIndexMatchCandidate) GetPhysicalGroupingPrefixCount

func (c *AggregateIndexMatchCandidate) GetPhysicalGroupingPrefixCount() int

GetPhysicalGroupingPrefixCount returns the number of grouping columns that occur before the aggregate value in the physical BY_GROUP key.

func (*AggregateIndexMatchCandidate) GetRecordTypes

func (c *AggregateIndexMatchCandidate) GetRecordTypes() []string

func (*AggregateIndexMatchCandidate) GetSargableAliases

func (c *AggregateIndexMatchCandidate) GetSargableAliases() []values.CorrelationIdentifier

func (*AggregateIndexMatchCandidate) GetTraversal

func (c *AggregateIndexMatchCandidate) GetTraversal() *Traversal

GetTraversal returns the Traversal of this candidate's expression tree, built lazily on first access via ExpandValueIndex (using the grouping columns as the index columns). The traversal is stable once computed (sync.Once).

func (*AggregateIndexMatchCandidate) GroupingSignature

func (c *AggregateIndexMatchCandidate) GroupingSignature() []byte

GroupingSignature returns the normalized encoding of the grouping key expression, or nil when it could not be derived.

func (*AggregateIndexMatchCandidate) IsUnique

func (c *AggregateIndexMatchCandidate) IsUnique() bool

func (*AggregateIndexMatchCandidate) MatchesGroupBy

MatchesGroupBy reports whether this aggregate index can directly satisfy the given GroupByExpression. Returns true when:

  • The grouping keys match the index's groupCols
  • The GroupBy has exactly one aggregate that matches the index's function + column

func (*AggregateIndexMatchCandidate) MatchesSingleAggregateOf

func (c *AggregateIndexMatchCandidate) MatchesSingleAggregateOf(gb *expressions.GroupByExpression, aggIndex int) bool

MatchesSingleAggregateOf reports whether this candidate's grouping keys match gb's grouping keys AND this candidate covers the aggregate at index aggIndex in gb's aggregate list. Used by the multi-aggregate intersection path: each candidate covers one aggregate while all share the same grouping columns.

func (*AggregateIndexMatchCandidate) NeedsGroupExistenceCompanion

func (c *AggregateIndexMatchCandidate) NeedsGroupExistenceCompanion() bool

NeedsGroupExistenceCompanion reports whether a GROUPED query over this index must be companion-joined to answer correctly.

func (*AggregateIndexMatchCandidate) PredicateSignature

func (c *AggregateIndexMatchCandidate) PredicateSignature() []byte

PredicateSignature returns the normalized encoding of the index's sparse predicate, nil for a dense index.

func (*AggregateIndexMatchCandidate) ToScanPlan

func (*AggregateIndexMatchCandidate) WithGroupExistence

func (c *AggregateIndexMatchCandidate) WithGroupExistence(countsRows bool, groupingSignature []byte) *AggregateIndexMatchCandidate

WithGroupExistence records the two structural facts RFC-209 needs about the underlying index: whether its stored value counts ROWS (a COUNT(*) index), and the normalized encoding of its grouping key expression. Both come from the record-layer Index and are supplied by the candidate builder; see the field docs for what each decides.

func (*AggregateIndexMatchCandidate) WithGroupExistenceCompanionNeed

func (c *AggregateIndexMatchCandidate) WithGroupExistenceCompanionNeed(
	predicateSignature []byte, needsCompanion bool,
) *AggregateIndexMatchCandidate

WithGroupExistenceCompanionNeed records the remaining two facts: the index's sparse-predicate signature (which a companion must match, or it counts a different row population) and whether a grouped query over this index needs a companion at all.

type AliasMap

type AliasMap struct {
	// contains filtered or unexported fields
}

AliasMap is an immutable bidirectional mapping between CorrelationIdentifiers. Used during graph traversal to track which alias on the "left" side corresponds to which alias on the "right" side when comparing or rebasing expressions.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.AliasMap.

Key use cases:

  • Subquery decorrelation: rebasing correlated references when pulling a subquery out of its enclosing context.
  • Expression equivalence: determining if two sub-graphs are semantically equal under an alias mapping.
  • Push/pull rules: translating alias references when moving operators across quantifier boundaries.

func AliasMapOfAliases

func AliasMapOfAliases(source, target values.CorrelationIdentifier) *AliasMap

AliasMapOfAliases creates a single-entry alias map.

func EmptyAliasMap

func EmptyAliasMap() *AliasMap

EmptyAliasMap returns the shared empty alias map.

func (*AliasMap) Compose

func (m *AliasMap) Compose(other *AliasMap) *AliasMap

Compose creates a new AliasMap where each source maps to the target of the target in `other`. If this maps A→B and other maps B→C, the result maps A→C.

func (*AliasMap) ContainsMapping

func (m *AliasMap) ContainsMapping(source, target values.CorrelationIdentifier) bool

ContainsMapping reports whether source maps to target.

func (*AliasMap) ContainsSource

func (m *AliasMap) ContainsSource(source values.CorrelationIdentifier) bool

ContainsSource reports whether source is mapped.

func (*AliasMap) ContainsTarget

func (m *AliasMap) ContainsTarget(target values.CorrelationIdentifier) bool

ContainsTarget reports whether target is mapped.

func (*AliasMap) Derived

func (m *AliasMap) Derived(additions *AliasMap) *AliasMap

Derived creates a new AliasMap that extends this one with additional mappings. Existing mappings are preserved; new mappings that conflict with existing ones are skipped.

func (*AliasMap) ForwardMap

func (m *AliasMap) ForwardMap() (values.AliasMap, error)

ForwardMap returns a validated immutable values-level alias map suitable for passing to values.RebaseValue. This is the checked bridge between the cascades-level AliasMap and the values-level rebase infrastructure: in particular, it rejects an attempted mapping between the reserved current correlation and an ordinary correlation.

func (*AliasMap) GetSource

GetSource returns the source for the given target, or the target itself if not mapped (identity fallback).

func (*AliasMap) GetTarget

GetTarget returns the target for the given source, or the source itself if not mapped (identity fallback).

func (*AliasMap) GetTargetOrEmpty

func (m *AliasMap) GetTargetOrEmpty(source values.CorrelationIdentifier) (values.CorrelationIdentifier, bool)

GetTargetOrEmpty returns the target for the given source, or empty if not mapped. Unlike GetTarget, does NOT fall back to identity.

func (*AliasMap) IsEmpty

func (m *AliasMap) IsEmpty() bool

IsEmpty reports whether the map contains no mappings.

func (*AliasMap) IsIdentity

func (m *AliasMap) IsIdentity() bool

IsIdentity reports whether all mappings are identity (source==target) or the map is empty.

func (*AliasMap) Size

func (m *AliasMap) Size() int

Size returns the number of mappings.

func (*AliasMap) Sources

func (m *AliasMap) Sources() []values.CorrelationIdentifier

Sources returns all source aliases.

type AliasMapBuilder

type AliasMapBuilder struct {
	// contains filtered or unexported fields
}

AliasMapBuilder constructs an AliasMap incrementally.

func NewAliasMapBuilder

func NewAliasMapBuilder() *AliasMapBuilder

NewAliasMapBuilder creates an empty builder.

func (*AliasMapBuilder) Build

func (b *AliasMapBuilder) Build() *AliasMap

Build creates an immutable AliasMap from the builder's state.

func (*AliasMapBuilder) Put

func (b *AliasMapBuilder) Put(source, target values.CorrelationIdentifier) bool

Put adds a source→target mapping. Returns false if either source or target is already mapped (would create an inconsistent bi-map).

func (*AliasMapBuilder) PutAll

func (b *AliasMapBuilder) PutAll(other *AliasMap)

PutAll copies all entries from other into this builder. Each entry is added via Put; conflicts (source or target already mapped) are silently skipped to match Java's Builder.putAll semantics.

Ports Java's AliasMap.Builder.putAll.

func (*AliasMapBuilder) PutAllChecked

func (b *AliasMapBuilder) PutAllChecked(other *AliasMap) bool

PutAllChecked copies all entries from other, returning false if ANY entry CONFLICTS with an existing mapping. It is ATOMIC: on conflict the builder is left UNCHANGED (all entries are verified before any is applied), so a caller that composes child bound maps into a quantifier bijection can try one candidate child match, and on rejection try the next on the SAME builder without a partial mutation leaking through. Unlike PutAll (which silently skips conflicts), this reports them — Java's match() only ever combines COMPATIBLE bound maps, never a contradictory merge.

func (*AliasMapBuilder) PutChecked

func (b *AliasMapBuilder) PutChecked(source, target values.CorrelationIdentifier) bool

PutChecked adds source→target, returning true if the mapping was added OR was already present IDENTICALLY, and false only on a genuine CONFLICT (source already maps to a different target, or target is already claimed by a different source). Unlike Put — which reports false for any pre-existing source/target, including an identical re-binding — PutChecked treats an identical duplicate as consistent. It is the conflict-aware primitive for composing quantifier bijections, where a child PartialMatch may legitimately re-assert a binding the composition already holds; only a contradictory binding invalidates the permutation.

type AliasMapValueEquivalence

type AliasMapValueEquivalence struct {
	// contains filtered or unexported fields
}

AliasMapValueEquivalence maps QuantifiedObjectValues via an AliasMap to enable cross-scope value comparison. Two values are equal if they are both QuantifiedObjectValues and their aliases are mapped in the AliasMap.

Ports Java's ValueEquivalence.AliasMapBackedValueEquivalence.

func NewAliasMapValueEquivalence

func NewAliasMapValueEquivalence(am *AliasMap) *AliasMapValueEquivalence

NewAliasMapValueEquivalence creates a ValueEquivalence backed by the given AliasMap.

func (*AliasMapValueEquivalence) IsDefinedEqual

func (e *AliasMapValueEquivalence) IsDefinedEqual(left, right values.Value) ConstrainedBoolean

IsDefinedEqual checks if two values are QuantifiedObjectValues with aliases mapped in the underlying AliasMap.

func (*AliasMapValueEquivalence) IsDefinedEqualAlias

func (e *AliasMapValueEquivalence) IsDefinedEqualAlias(left, right values.CorrelationIdentifier) ConstrainedBoolean

IsDefinedEqualAlias checks if two correlation identifiers are mapped in the underlying AliasMap.

type AndAbsorbOrRule

type AndAbsorbOrRule struct {
	// contains filtered or unexported fields
}

AndAbsorbOrRule: inside an AND, any OR child that contains a sibling is redundant — drop it. `AND(p, OR(p, q))` → `AND(p)` → `p` once the constant-fold rules collapse the unary AND.

func NewAndAbsorbOrRule

func NewAndAbsorbOrRule() *AndAbsorbOrRule

NewAndAbsorbOrRule constructs the rule.

func (*AndAbsorbOrRule) Matcher

func (*AndAbsorbOrRule) OnMatch

func (r *AndAbsorbOrRule) OnMatch(call *RuleCall)

type AndConstantSimplifyRule

type AndConstantSimplifyRule struct {
	// contains filtered or unexported fields
}

AndConstantSimplifyRule matches an AndPredicate and folds constant children per Kleene AND identities.

func NewAndConstantSimplifyRule

func NewAndConstantSimplifyRule() *AndConstantSimplifyRule

NewAndConstantSimplifyRule constructs the rule.

func (*AndConstantSimplifyRule) Matcher

func (*AndConstantSimplifyRule) OnMatch

func (r *AndConstantSimplifyRule) OnMatch(call *RuleCall)

type AndDedupRule

type AndDedupRule struct {
	// contains filtered or unexported fields
}

AndDedupRule removes structurally-equal duplicate children from an AndPredicate. `AND(p, p, q, p)` → `AND(p, q)`. Mirrors Java `PredicateSimplification`'s dedup pass.

func NewAndDedupRule

func NewAndDedupRule() *AndDedupRule

NewAndDedupRule constructs the rule.

func (*AndDedupRule) Matcher

func (r *AndDedupRule) Matcher() matching.BindingMatcher

func (*AndDedupRule) OnMatch

func (r *AndDedupRule) OnMatch(call *RuleCall)

type AndFlattenRule

type AndFlattenRule struct {
	// contains filtered or unexported fields
}

AndFlattenRule normalises nested AndPredicates: `AND(AND(a, b), c)` → `AND(a, b, c)`. Mirrors Java's associative-flatten in `ValueSimplificationRuleSet`. Runs before the constant-simplify pass so the simplifier sees a flat list of operands.

func NewAndFlattenRule

func NewAndFlattenRule() *AndFlattenRule

NewAndFlattenRule constructs the rule.

func (*AndFlattenRule) Matcher

func (r *AndFlattenRule) Matcher() matching.BindingMatcher

func (*AndFlattenRule) OnMatch

func (r *AndFlattenRule) OnMatch(call *RuleCall)

type BestMemberSelector

type BestMemberSelector interface {
	// BestMember returns the chosen best member for `ref`, or nil
	// if the selector has no stored choice.
	BestMember(ref *expressions.Reference) expressions.RelationalExpression
	// HasBestMember reports whether the selector has a stored
	// choice for `ref` (distinguishes "no choice" from "chose nil").
	HasBestMember(ref *expressions.Reference) bool
}

BestMemberSelector is the optional interface a planner implements to expose its OPTIMIZE-chosen best member per Reference. ExtractBestPlanFromSelector consults the selector first; falls back to cost-comparator selection when the selector reports no stored choice.

Used by the cascades.Planner via ExtractBestPlanFromSelector to avoid recomputing CostLess for every Reference (planner's OPTIMIZE phase already did the work).

type BiMap

type BiMap[K any, V any] struct {
	// contains filtered or unexported fields
}

BiMap is a bidirectional map: every key maps to exactly one value, and every value maps to exactly one key. Mirrors Guava's ImmutableBiMap used in Java's GroupByMappings and related matching infrastructure.

Unlike Go's built-in maps which use pointer identity for interface keys, BiMap uses configurable stringifier functions to determine equality — matching Java's equals()/hashCode() semantics.

func NewBiMap

func NewBiMap[K comparable, V comparable]() *BiMap[K, V]

NewBiMap creates an empty BiMap for comparable types using fmt.Sprintf as the stringifier. This preserves backward compatibility for simple types like string, int, etc.

func NewBiMapFromMap

func NewBiMapFromMap[K comparable, V comparable](m map[K]V) *BiMap[K, V]

NewBiMapFromMap creates a BiMap from a regular map. Panics if the map contains duplicate values (violating the bijection constraint).

func NewBiMapWith

func NewBiMapWith[K any, V any](keyStr func(K) string, valStr func(V) string) *BiMap[K, V]

NewBiMapWith creates an empty BiMap with the given stringifier functions for keys and values.

func NewCorrValueBiMap

func NewCorrValueBiMap() *BiMap[values.CorrelationIdentifier, values.Value]

NewCorrValueBiMap creates a BiMap[values.CorrelationIdentifier, values.Value] using Name() for correlation identifiers and ExplainValue for values.

func NewValueBiMap

func NewValueBiMap() *BiMap[values.Value, values.Value]

NewValueBiMap creates a BiMap[values.Value, values.Value] using ExplainValue for structural equality — the standard representation used throughout the codebase for plan equality and hashing.

func (*BiMap[K, V]) Copy

func (b *BiMap[K, V]) Copy() *BiMap[K, V]

Copy returns a deep copy of the BiMap.

func (*BiMap[K, V]) Get

func (b *BiMap[K, V]) Get(key K) (V, bool)

Get returns the value for the given key and whether it was found.

func (*BiMap[K, V]) GetInverse

func (b *BiMap[K, V]) GetInverse(value V) (K, bool)

GetInverse returns the key for the given value and whether it was found.

func (*BiMap[K, V]) Len

func (b *BiMap[K, V]) Len() int

Len returns the number of entries.

func (*BiMap[K, V]) Put

func (b *BiMap[K, V]) Put(key K, value V)

Put inserts a key-value pair. Panics if the value already maps to a different key (bijection constraint).

func (*BiMap[K, V]) PutAll

func (b *BiMap[K, V]) PutAll(other *BiMap[K, V])

PutAll copies all entries from other into this BiMap. Panics if any value in other already maps to a different key in this BiMap.

func (*BiMap[K, V]) Range

func (b *BiMap[K, V]) Range(fn func(key K, value V) bool)

Range iterates over all key-value pairs. If fn returns false, iteration stops.

type BitSet

type BitSet struct {
	// contains filtered or unexported fields
}

BitSet is a simple bit set backed by a map, providing the subset of java.util.BitSet operations needed by the intersection sieve logic.

func NewBitSet

func NewBitSet() *BitSet

NewBitSet creates an empty BitSet.

func (*BitSet) And

func (b *BitSet) And(other *BitSet) *BitSet

And returns a new BitSet that is the intersection of b and other.

func (*BitSet) Cardinality

func (b *BitSet) Cardinality() int

Cardinality returns the number of set bits.

func (*BitSet) Equal

func (b *BitSet) Equal(other *BitSet) bool

Equal reports whether b and other have exactly the same set bits.

func (*BitSet) Get

func (b *BitSet) Get(pos int) bool

Get returns whether the bit at position pos is set.

func (*BitSet) IsSubsetOf

func (b *BitSet) IsSubsetOf(other *BitSet) bool

IsSubsetOf reports whether every bit in b is also set in other.

func (*BitSet) Or

func (b *BitSet) Or(other *BitSet) *BitSet

Or returns a new BitSet that is the union of b and other.

func (*BitSet) Set

func (b *BitSet) Set(pos int)

Set sets the bit at position pos.

func (*BitSet) String

func (b *BitSet) String() string

String returns a human-readable representation of the set bits in ascending order, e.g. "{0, 2, 5}".

type CascadesRule

type CascadesRule interface {
	// Matcher returns the pattern this rule fires on. The planner
	// walks every expression in the memo, runs every rule's
	// matcher against it, and invokes OnMatch on successful
	// bindings.
	Matcher() matching.BindingMatcher

	// OnMatch is the rule body. It reads call.Bindings to retrieve
	// the matched expression shape and calls call.Yield for each
	// replacement it produces. Zero yields is legal — the rule
	// simply declined to fire for this match.
	OnMatch(call *RuleCall)
}

CascadesRule is the transform interface. Concrete rules implement Matcher + OnMatch. Rule authors compose the matcher using the combinators in combinators.go + the Instance/AnyValue/Arithmetic matchers in matcher.go.

func DefaultSimplifyRules

func DefaultSimplifyRules() []CascadesRule

DefaultSimplifyRules returns the canonical simplification rule set. Callers pass this to Simplify for a typical "flatten + constant-fold + identity-drop" pass. Order matters: flattens run first so the constant-fold rules see a flat operand list; then Comparison constants fold; then Not resolves; then the And/Or identity-drop + absorbing-element rules.

Rules NOT included (intentional):

  • De Morgan NOT-distribution (`NOT(AND(a,b))` → `OR(NOT a, NOT b)`). Kleene-safe but NODE-INCREASING (N-ary AND becomes N-element OR plus N NOTs), so it doesn't fit the strict-reduction termination invariant DefaultSimplifyRules guarantees. Java applies De Morgan as a separate `BooleanNormalizer` pre-CNF pass; we mirror this via the explicit `NormalizationRules()` rule set (which prepends `NewDeMorganRule()` before the default set).
  • Tautology / contradiction folds that require NOT-NULL metadata (`x = x` → TRUE iff x is NOT NULL). Waits on Type nullability tracking.

func NormalizationRules

func NormalizationRules() []CascadesRule

NormalizationRules is the rule set to use when De Morgan distribution + nested-NOT push-down are desired in addition to the default simplification pass. Java's BooleanNormalizer applies these as a separate pre-CNF normalisation; callers wanting the same effect compose `Simplify(pred, NormalizationRules())` with the existing Simplify driver.

Order matters:

  • DeMorganRule comes BEFORE the default rules so the AND/OR boundaries surface for the constant-fold / identity-drop follow-on passes.
  • DefaultSimplifyRules then collapses the resulting AND/OR/NOT leaves further (NotComparisonRewriteRule turns NOT(=) into <>, NotConstantSimplifyRule double-negates, etc.).

type ComparisonConstantSimplifyRule

type ComparisonConstantSimplifyRule struct {
	// contains filtered or unexported fields
}

ComparisonConstantSimplifyRule folds a ComparisonPredicate whose operand is a ConstantValue — both sides of the comparison are known at plan time, so the predicate evaluates to a constant. Mirrors Java's `ValueSimplificationRuleSet` constant-predicate short-circuits.

Example: `5 = 3` → `FALSE`, `7 > 2` → `TRUE`, `NULL = 1` → `UNKNOWN`. Only fires when both sides are known-constant Values — leaf constants or all-constant composites folded via EvaluateConstant (see constantLiteral / values.IsConstantValue) — i.e. Values whose result is reproducible without an eval context.

func NewComparisonConstantSimplifyRule

func NewComparisonConstantSimplifyRule() *ComparisonConstantSimplifyRule

NewComparisonConstantSimplifyRule constructs the rule.

func (*ComparisonConstantSimplifyRule) Matcher

func (*ComparisonConstantSimplifyRule) OnMatch

func (r *ComparisonConstantSimplifyRule) OnMatch(call *RuleCall)

type CompensatedResult

type CompensatedResult struct {
	Impossible           bool
	ResultCompensationFn *ResultCompensationFunction
	GroupByMappings      *GroupByMappings
}

CompensatedResult bundles the results of computing result compensation for a partial match. Ports Java's Compensation.CompensatedResult.

func ComputeResultCompensation

func ComputeResultCompensation(pm PartialMatch, rootOfMatchPullUp *PullUp) *CompensatedResult

ComputeResultCompensation computes the result compensation for the top operation's partial match. Ports Java's Compensation.computeResultCompensation.

type Compensation

type Compensation interface {
	// IsNeeded reports whether this compensation must be applied.
	// A ForMatch compensation can also report false when all of its
	// components are empty.
	IsNeeded() bool

	// IsImpossible reports whether this compensation cannot be applied.
	// Returns true only for ImpossibleCompensation or a ForMatch with
	// impossible=true.
	IsImpossible() bool

	// IsNeededForFiltering reports whether this compensation needs to
	// be applied for correct filtering. This matters when a caller cares
	// about the correct set of rows but not the result shape (e.g.
	// EXISTS predicates).
	IsNeededForFiltering() bool

	// IsFinalNeeded reports whether final (result-shape) compensation
	// must be applied when this compensation is at the top of a
	// compensation chain.
	IsFinalNeeded() bool

	// CanBeDeferred reports whether this compensation can be combined
	// with subsequent compensations further up the matched DAG or
	// whether it must be applied at the exact position that created it.
	CanBeDeferred() bool
}

Compensation is the byproduct of expression DAG matching. When a query subgraph Q matches a materialized data set M (e.g. an index), M may subsume Q but produce extraneous records. A Compensation corrects for that by applying post-operations such as filtering, distinct-ing, or reshaping results.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.Compensation.

ForMatchCompensation implements Intersect, Union, Apply, and ApplyFinal.

var (
	// NoCompensation indicates that no additional operators need to be
	// injected to compensate for a match. Equivalent to Java's
	// Compensation.NO_COMPENSATION.
	NoCompensation Compensation = noCompensation{}

	// ImpossibleCompensation indicates that compensation is needed but
	// cannot be computed. It is the identity element for the
	// intersection monoid on compensations. Equivalent to Java's
	// Compensation.IMPOSSIBLE_COMPENSATION.
	ImpossibleCompensation Compensation = impossibleCompensation{}
)

func IntersectCompensations

func IntersectCompensations(compensations []Compensation) Compensation

IntersectCompensations folds a slice of Compensations via the intersection monoid. The identity element is ImpossibleCompensation. Ports Java's `compensations.stream().reduce(impossibleCompensation, Compensation::intersect)`.

func UnionCompensations

func UnionCompensations(compensations []Compensation) Compensation

UnionCompensations folds a slice of Compensations via union. The identity element is NoCompensation.

type ConstrainedBoolean

type ConstrainedBoolean struct {
	Value      bool
	Constraint *QueryPlanConstraint
}

ConstrainedBoolean is a boolean result that may carry an optional QueryPlanConstraint. Ports Java's ConstrainedBoolean.

func AlwaysTrue

func AlwaysTrue() ConstrainedBoolean

AlwaysTrue returns a ConstrainedBoolean that is unconditionally true.

func FalseValue

func FalseValue() ConstrainedBoolean

FalseValue returns a ConstrainedBoolean that is false.

func TrueWithConstraint

func TrueWithConstraint(c *QueryPlanConstraint) ConstrainedBoolean

TrueWithConstraint returns a ConstrainedBoolean that is true only if the given constraint holds.

func ValueSemanticEquals

func ValueSemanticEquals(a, b values.Value, veq ValueEquivalence) ConstrainedBoolean

ValueSemanticEquals compares two Values for semantic equality using the given ValueEquivalence for cross-scope comparisons. Returns a ConstrainedBoolean that may carry a QueryPlanConstraint.

The algorithm:

  1. Pointer equality → always true.
  2. EqualsWithoutChildren + recursive child comparison via ValueSemanticEquals (composing constraints).
  3. If structural comparison fails, falls back to valueEquivalence.IsDefinedEqual (axiom-based equality).

Ports Java's Value.semanticEquals(other, ValueEquivalence).

func (ConstrainedBoolean) ComposeWithOther

func (cb ConstrainedBoolean) ComposeWithOther(other ConstrainedBoolean) ConstrainedBoolean

ComposeWithOther composes this ConstrainedBoolean with another via AND semantics: if either is false, the result is false. If both are true, constraints are merged. Ports Java's ConstrainedBoolean.composeWithOther().

func (ConstrainedBoolean) Filter

Filter returns false if the ConstrainedBoolean is false; otherwise returns the result of evaluating the predicate fn. Ports Java's ConstrainedBoolean.filter().

func (ConstrainedBoolean) IsFalse

func (cb ConstrainedBoolean) IsFalse() bool

IsFalse reports whether the boolean value is false. Ports Java's ConstrainedBoolean.isFalse().

func (ConstrainedBoolean) IsTrue

func (cb ConstrainedBoolean) IsTrue() bool

IsTrue reports whether the boolean value is true (regardless of constraint). Ports Java's ConstrainedBoolean.isTrue().

type ConstraintMap

type ConstraintMap struct {
	// contains filtered or unexported fields
}

ConstraintMap holds constraints per Reference. Rules read constraints from the map and push new constraints for child References.

func NewConstraintMap

func NewConstraintMap() *ConstraintMap

NewConstraintMap creates an empty constraint map.

type CrossProductIterator

type CrossProductIterator[T any] struct {
	// contains filtered or unexported fields
}

CrossProductIterator lazily enumerates the Cartesian product with support for skipping entire subtrees. When an incremental merge fails at depth d, Skip(d) advances past all combinations sharing the same prefix up to position d.

func NewCrossProductIterator

func NewCrossProductIterator[T any](lists [][]T) *CrossProductIterator[T]

func (*CrossProductIterator[T]) HasNext

func (it *CrossProductIterator[T]) HasNext() bool

func (*CrossProductIterator[T]) Next

func (it *CrossProductIterator[T]) Next() []T

func (*CrossProductIterator[T]) Skip

func (it *CrossProductIterator[T]) Skip(depth int)

Skip advances past all combinations sharing the same prefix up to position (depth-1). Equivalent to incrementing indices[depth-1] and resetting all deeper positions.

type DeMorganRule

type DeMorganRule struct {
	// contains filtered or unexported fields
}

DeMorganRule applies De Morgan's law to push a NOT past an AND or OR boundary:

NOT(AND(a, b, c)) ≡ OR (NOT a, NOT b, NOT c)
NOT(OR (a, b, c)) ≡ AND(NOT a, NOT b, NOT c)

Mirrors Java's `DeMorgansTheoremRule`. Kleene-safe: each child term is independently negated and the major is swapped, preserving 3VL semantics. Reducing-after-fold: subsequent NotConstantSimplifyRule + AndConstantSimplifyRule passes can collapse the negated tree further (`NOT TRUE` → FALSE, `OR(FALSE, FALSE)` → FALSE, etc.).

**Not part of DefaultSimplifyRules.** Java applies De Morgan as a separate normalisation pass (`BooleanNormalizer`); the DefaultSimplifyRules set runs only constant-fold + identity-drop + absorbing-element + leaf-NOT-rewrite. Use NormalizationRules() (or build a custom rule list) when De Morgan is desired.

func NewDeMorganRule

func NewDeMorganRule() *DeMorganRule

NewDeMorganRule constructs the rule.

func (*DeMorganRule) Matcher

func (r *DeMorganRule) Matcher() matching.BindingMatcher

func (*DeMorganRule) OnMatch

func (r *DeMorganRule) OnMatch(call *RuleCall)

type DecorrelateValuesRule

type DecorrelateValuesRule struct {
	// contains filtered or unexported fields
}

DecorrelateValuesRule is an exploration rule that inlines "values boxes" into the parent SelectExpression. A values box is a quantifier over a SelectExpression whose result value is constant (uncorrelated to its own child quantifier) — typically produced by parameterized function inlining or constant subqueries.

Pattern:

SelectExpression(
  ForEach(alias=A) → Ref[SelectExpression(constResult, [ForEach → valuesSource], [])],
  ForEach(alias=B) → Ref[other],
  predicates referencing A
)

Rewrite: replace references to alias A with the constant result value from the values box, remove the values box quantifier.

SelectExpression(
  ForEach(alias=B) → Ref[other],
  predicates with A→constResult substitution
)

Ports Java's DecorrelateValuesRule (ExplorationCascadesRule).

func NewDecorrelateValuesRule

func NewDecorrelateValuesRule() *DecorrelateValuesRule

func (*DecorrelateValuesRule) Matcher

func (*DecorrelateValuesRule) OnMatch

func (r *DecorrelateValuesRule) OnMatch(call *ExpressionRuleCall)

type DistinctMergeRule

type DistinctMergeRule struct {
	// contains filtered or unexported fields
}

DistinctMergeRule collapses LogicalDistinct(LogicalDistinct(X)) into a single LogicalDistinct(X) — DISTINCT is idempotent.

Java equivalent: there's no dedicated rule (the Cascades cost model would naturally prefer the single-Distinct shape); Go applies the uncontroversial static rewrite directly.

func NewDistinctMergeRule

func NewDistinctMergeRule() *DistinctMergeRule

NewDistinctMergeRule constructs the rule with its pattern matcher pre-built.

func (*DistinctMergeRule) Matcher

Matcher returns the pattern.

func (*DistinctMergeRule) OnMatch

func (r *DistinctMergeRule) OnMatch(call *ExpressionRuleCall)

OnMatch examines the matched LogicalDistinct; if its inner is also a LogicalDistinct, yields a single LogicalDistinct over the inner-of- inner. Otherwise, declines.

type DistinctOverGroupByElimRule

type DistinctOverGroupByElimRule struct {
	// contains filtered or unexported fields
}

DistinctOverGroupByElimRule eliminates a LogicalDistinct that sits directly over a GroupByExpression when the grouping key's physical identity is congruent with logical DISTINCT equality. GROUP BY's streaming/runtime and maintained aggregate indexes preserve raw FLOAT/DOUBLE NaN encodings, while DISTINCT canonicalizes NaNs, so floating (including nested floating) or unknown keys must keep the outer DISTINCT.

Distinct(GroupBy(keys, aggs, X))  →  GroupBy(keys, aggs, X)

Java equivalent: part of RemoveRedundantDistinctRule family.

func NewDistinctOverGroupByElimRule

func NewDistinctOverGroupByElimRule() *DistinctOverGroupByElimRule

func (*DistinctOverGroupByElimRule) Matcher

func (*DistinctOverGroupByElimRule) OnMatch

type DistinctOverSortElimRule

type DistinctOverSortElimRule struct {
	// contains filtered or unexported fields
}

DistinctOverSortElimRule eliminates a LogicalSort that sits directly under a LogicalDistinct.

Distinct(Sort([k1, k2, ...], X))  →  Distinct(X)

SQL semantic justification: DISTINCT operates on a row-set (unordered); the inner Sort's ordering is lost the moment Distinct dedupes. So the inner sort is wasted work.

Edge case — Sort([]) (Unsorted): the inner is a no-op anyway. UnsortedSortElim handles it independently. This rule firing on that case is harmless (yields Distinct(unsorted's inner) = same thing UnsortedSortElim would have produced post-merge).

Termination: yields Distinct(sort.GetInner()) — REUSES the inner Sort's Quantifier (and therefore its Reference pointer). Second fire of the rule on the same input member produces a structurally- identical Distinct (same children-Reference pointers), so Reference.Insert's sameChildReferences dedup absorbs it.

Java equivalent: emerges from cost preference for cheaper plans. Seed implements directly so the optimiser produces the concretely-cheaper logical tree before B4 cost lands.

func NewDistinctOverSortElimRule

func NewDistinctOverSortElimRule() *DistinctOverSortElimRule

NewDistinctOverSortElimRule constructs the rule.

func (*DistinctOverSortElimRule) Matcher

Matcher returns the pattern.

func (*DistinctOverSortElimRule) OnMatch

func (r *DistinctOverSortElimRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the inner Quantifier ranges over a LogicalSortExpression. Yields Distinct over the sort's inner Quantifier directly (NOT a fresh wrap — see Termination note).

type DistinctOverUnionDedupRule

type DistinctOverUnionDedupRule struct {
	// contains filtered or unexported fields
}

DistinctOverUnionDedupRule removes structurally-equivalent siblings from a LogicalUnion that sits inside a LogicalDistinct.

Distinct(Union(A, B, A')) where A SemanticEquals A'
→
Distinct(Union(A, B))

Soundness: the outer Distinct dedupes the FULL row stream, so any duplicate rows contributed by equivalent siblings get squashed. Producing them in the first place is wasted work. SQL-equivalent: DISTINCT(UNION ALL of equivalent operands) = DISTINCT(union of distinct ones).

Why we don't apply this directly to bare LogicalUnion (without an outer Distinct): UNION ALL semantics PRESERVE duplicates. Removing equivalent siblings would silently change row counts.

Termination: the rule yields a Distinct wrapping a Quantifier over a FRESH Reference holding the deduped Union. The fresh Reference would defeat the sameChildReferences pointer-identity dedup, but Reference.Insert's SemanticEquals fallback (post-680e664a) catches the structural equivalence and absorbs the second yield. Without that fallback, this rule non-terminates — the original revert in c789c211 documents the failure.

func NewDistinctOverUnionDedupRule

func NewDistinctOverUnionDedupRule() *DistinctOverUnionDedupRule

NewDistinctOverUnionDedupRule constructs the rule.

Matches the FULL-ROW LogicalDistinctExpression; the OR path now emits the PK-dedup LogicalUniqueExpression, so this no longer fires for it. See TODO.md, "The ordered OR-union alternative is structurally unreachable".

func (*DistinctOverUnionDedupRule) Matcher

Matcher returns the pattern.

func (*DistinctOverUnionDedupRule) OnMatch

OnMatch fires when the inner is a LogicalUnion with at least one pair of structurally-equivalent siblings.

type EliminateNullOnEmptyRule

type EliminateNullOnEmptyRule struct {
	// contains filtered or unexported fields
}

EliminateNullOnEmptyRule drops the null-on-empty flag from a SelectExpression's ForEach quantifier when some predicate of the surrounding SELECT PROVABLY REJECTS the null tuple that the null-on-empty quantifier would inject at that quantifier's alias.

Ports Java's EliminateNullOnEmptyRule (#4186), which REPLACED the buggy PullUpNullOnEmptyRule. PullUp's "positional-predicate-equality heuristic" (predicates.equals(otherPredicates)) was wrong with predicates that ACCEPT the injected null tuple (`… WHERE x IS NULL` over a null-on-empty leg) — it assumed the null tuple is always rejected. The correct test is semantic: substitute a typed NullValue at the quantifier's alias, constant-fold the predicate, and the quantifier is eligible iff the fold is FALSE or NULL (both filter the row out).

Per-alias: a null-rejecting predicate over alias A eliminates A's null-on-empty flag while leaving a null-ACCEPTING sibling (`B IS NULL`) over alias B intact.

Reachability: RewriteOuterJoinRule dissolves an outer-join box into an INNER select whose null-supplying leg rides a null-on-empty ForEach quantifier, so this rule is live on any LEFT/FULL-box query whose WHERE provably rejects the null tuple at that leg (`... LEFT JOIN b ... WHERE b.k = 110`). Stripping the flag is also what unlocks partitioning: PartitionBinarySelectRule declines to absorb predicates into a null-on-empty leg (post-box WHERE must not move inside the box), so the null-rejecting case flows ENOE-first, then partitions the plain twin.

func NewEliminateNullOnEmptyRule

func NewEliminateNullOnEmptyRule() *EliminateNullOnEmptyRule

NewEliminateNullOnEmptyRule constructs the rule.

func (*EliminateNullOnEmptyRule) Matcher

func (*EliminateNullOnEmptyRule) OnMatch

func (r *EliminateNullOnEmptyRule) OnMatch(call *ExpressionRuleCall)

type ExploreExprTask

type ExploreExprTask struct {
	Phase PlannerPhase
	Ref   *expressions.Reference
	Expr  expressions.RelationalExpression
	// contains filtered or unexported fields
}

ExploreExprTask pushes rule-transform tasks and child-exploration tasks for a single (group, expression) pair. Mirrors Java's AbstractExploreExpression.

func (*ExploreExprTask) Run

func (t *ExploreExprTask) Run(ctx context.Context, p *Planner)

type ExploreGroupTask

type ExploreGroupTask struct {
	Phase PlannerPhase
	Ref   *expressions.Reference
	// contains filtered or unexported fields
}

ExploreGroupTask explores a Reference within a phase. If the Reference's stage is behind the phase's target, it calls advancePlannerStage to transition. Then pushes exploration tasks for all members. Mirrors Java's CascadesPlanner.ExploreGroup.

func (*ExploreGroupTask) Run

func (t *ExploreGroupTask) Run(ctx context.Context, p *Planner)

type ExpressionMatchAdjuster

type ExpressionMatchAdjuster interface {
	// AdjustMatch attempts to refine the given PartialMatch by
	// absorbing this expression on the candidate side. Returns a new
	// MatchInfo on success, or nil if the expression cannot be
	// absorbed.
	AdjustMatch(pm PartialMatch) MatchInfo
}

ExpressionMatchAdjuster is an optional interface that a RelationalExpression can implement to support match adjustment. When the AdjustMatches pass encounters a candidate expression that implements this interface, it calls AdjustMatch to produce a refined MatchInfo wrapping the existing PartialMatch's MatchInfo.

In Java, this is the default method RelationalExpression.adjustMatch(PartialMatch) which returns Optional.empty(). Concrete overrides exist on MatchableSortExpression and SelectExpression.

type ExpressionMatcher

type ExpressionMatcher[T expressions.RelationalExpression] struct {
	// contains filtered or unexported fields
}

ExpressionMatcher type-asserts a candidate against a specific RelationalExpression concrete type T and binds the host on match. Counterpart to the existing `predicateMatcher[T]` for QueryPredicate rules — same shape, different type bound.

Used by RelationalExpression-shaped rules (FilterMergeRule, PushFilterThroughDistinctRule, and the rest of the rule_*.go files).

func NewExpressionMatcher

func NewExpressionMatcher[T expressions.RelationalExpression](rootType string) *ExpressionMatcher[T]

NewExpressionMatcher constructs a typed matcher for the given RelationalExpression subtype. Each call returns a distinct allocation so pointer-identity comparisons stay distinct across rule instances.

func (*ExpressionMatcher[T]) BindMatches

func (m *ExpressionMatcher[T]) BindMatches(outer *matching.PlannerBindings, in any) []*matching.PlannerBindings

BindMatches type-asserts `in` to T; on success binds m → in in the outer bindings and returns one new binding set. On failure returns nil.

func (*ExpressionMatcher[T]) RootOperator

func (m *ExpressionMatcher[T]) RootOperator() reflect.Type

RootOperator returns the one concrete type BindMatches admits — computed from the type parameter, so it can never drift from the type assert. Implements matching.RootOperatorMatcher for the planner's rule index.

An INTERFACE type parameter (ExpressionMatcher[RelationalExpression] — the match-anything shape MatchLeafRule / MatchIntermediateRule use) returns nil: the assert admits every implementor, so the rule belongs in the index's always bucket. Returning the interface type instead would bucket the rule under a key no concrete expression ever looks up — silently disabling the matching infrastructure (Java models this as PlannerRule.getRootOperator returning Optional.empty()).

func (*ExpressionMatcher[T]) RootType

func (m *ExpressionMatcher[T]) RootType() string

RootType returns the rule's debug-friendly root identifier.

type ExpressionRule

type ExpressionRule interface {
	Matcher() matching.BindingMatcher
	OnMatch(call *ExpressionRuleCall)
}

ExpressionRule is the transform interface for RelationalExpression- shaped rules. Counterpart to CascadesRule for the QueryPredicate / Value-shaped rules. Each impl provides:

  • Matcher: pattern the rule fires on (typically an ExpressionMatcher for the rule's root expression type).
  • OnMatch: rule body — reads call.Bindings via Get[T] / Get and calls call.Yield(replacement) for each rewritten expression.

func BatchAExpressionRules

func BatchAExpressionRules() []ExpressionRule

BatchAExpressionRules returns the physical-implementation rules (PrimaryScanRule, ImplementFilterRule, etc.). These fire during the PLANNING phase via WithPlanningExpressionRules. They yield to InsertFinal so their results land in FinalMembers.

Uses Java's PlanningRuleSet.IMPLEMENTATION_RULES as its base. Documented Go extensions are included where the read-side architecture differs.

A BARE `UNION ALL` is implemented by ImplementUnorderedUnionRule alone, as in Java. Go also had ImplementUnionRule, emitting a second concat plan for the SAME logical shape at the SAME cost — the two are both operator-neutral — so which one won was decided by exploration order rather than by cost, and it flipped on unrelated changes. Java has no such rule (its RecordQueryUnionPlan variants require compatible comparison keys and arise from ordered/ distinct-union planning), and the Go rule produced a keyless concat every time. THAT is the reason for the deletion, and it stands on its own.

AN EARLIER VERSION OF THIS NOTE ALSO CLAIMED RecordQueryUnionPlan "is an eager concat that DECLINES a continuation", and the executor says otherwise. executeUnion routes to executeUnionStreaming whenever every branch's column names are statically known — lazy, per-branch CursorFactory, resuming off a branch-tagged ConcatContinuation. Only executeUnionBuffered declines, and only on the fallback where the names are not statically known. On the eagerness axis the deletion in fact moved the OTHER way: the surviving executeUnorderedUnion opens every child cursor up front. Keeping the wrong reason next to a right conclusion is how a later reader talks themselves into reverting the conclusion.

It did serve one shape alone, and only because of a second Go-only divergence: ImplementUnorderedUnionRule carried a two-leg floor Java's rule does not have, so a ONE-leg logical union that survived UnionSingletonElimRule had no implementer. The floor is gone (see that rule), which is what makes the unordered rule sufficient on its own.

func DMLImplementationRules

func DMLImplementationRules() []ExpressionRule

DMLImplementationRules returns the DML-side implementation rules (ImplementInsertRule, ImplementDeleteRule). Mirrors Java's per-DML implementation rule set.

Compose with: append(rules, DMLImplementationRules()...) when the planner needs to physical-implement DML expressions (INSERT / DELETE / UPDATE).

All 3 DML implement rules now ported (Insert / Delete / Update). Per-row transform application for Update happens at execution time, not rule-fire time — transforms pass through unchanged.

func DefaultExpressionRules

func DefaultExpressionRules() []ExpressionRule

DefaultExpressionRules returns the REWRITING-phase exploration rule set the optimiser fires by default: uncontroversial logical-to-logical rewrites that don't need cost-aware decisions (the physical-implementation rules live in BatchAExpressionRules / DefaultImplementationRules, PLANNING phase). Order matters within an exploration round: merge-rules run first (collapse nested operators of the same kind), then no-op-elimination rules run on the merged shapes. Order across rounds doesn't matter because the task-stack driver re-fires every rule against newly-yielded members until the group saturates.

**Alias rebasing caveat**: every Push*Through* and Pull*Above* rule in this set moves a Filter (or TypeFilter) across a Quantifier boundary without rewriting the moved operator's internal alias references — predicates and Values inside the moved operator keep referencing the ORIGINAL quantifier's alias (no TranslationMap rebase at the move site). Harmless for row-set semantics (no evaluator resolves the moved predicates through those aliases), but a rule that inspects correlation structure (GetCorrelatedToWithoutChildren) on a pushed predicate sees the stale alias. Correlation-aware logic must not consume pushed/pulled predicates without a TranslationMap rebase (translation_map.go) first.

**ProjectionMergeRule's soundness contract**: the merge `Projection(P1) over Projection(P2) over X → Projection(P1) over X` is sound only because LogicalProjectionExpression's GetResultValue() passes the inner row through (projection is a pure side channel). If projection ever narrows the row shape (materialized projections), P1's Values may reference computed columns that only exist in P2's output — at that point the rule needs a column-substitution rewrite path or it must leave the default set. See ProjectionMergeRule's own doc for the per-rule discussion.

Each call returns a fresh slice — callers may mutate freely. Each element is a fresh rule instance — see NewXxxRule constructors for the per-call allocation contract.

func LookupRule

func LookupRule(name string) ExpressionRule

LookupRule returns the rule registered under `name`, or nil if not found.

func MatchingRules

func MatchingRules() []ExpressionRule

MatchingRules returns the matching rules that seed the partial-match infrastructure. These fire during the EXPLORE phase alongside expression rules but serve a different purpose: they establish PartialMatch instances between the query graph and MatchCandidate traversals, which are then consumed by implementation rules to produce index-scan plans.

Mirrors Java's PlanningRuleSet.MATCHING_RULES:

  • MatchLeafRule: seeds leaf-to-leaf matches
  • MatchIntermediateRule: composes child matches into parent matches

Compose with: append(DefaultExpressionRules(), MatchingRules()...)

func PlanningExplorationRules

func PlanningExplorationRules() []ExpressionRule

PlanningExplorationRules returns the exploration rules that re-fire during the PLANNING phase after advancePlannerStage clears EXPLORE artifacts. These re-derive logical alternatives from the promoted canonical seed. Mirrors Java's PlanningRuleSet.EXPLORATION_RULES.

func RegisterRule

func RegisterRule(name string, r ExpressionRule) ExpressionRule

RegisterRule adds the rule to the package-level registry under `name`. Returns the rule unchanged so callers can inline the register call:

var myRule = RegisterRule("MyRule", &MyRule{...})

Panics on duplicate name — registry collision is a programmer error, not runtime data.

func RewritingRules

func RewritingRules() []ExpressionRule

RewritingRules returns the exploration rules for the REWRITING phase. Mirrors Java's RewritingRuleSet.EXPLORATION_RULES:

  • QueryPredicateSimplificationRule: constant-fold predicate values
  • PredicatePushDownRule: push predicates into child quantifiers
  • DecorrelateValuesRule: inline constant value boxes

These rules are NOT part of DefaultExpressionRules — they target the REWRITING phase, which runs before the main PLANNING phase to normalise the expression tree. The planner driver composes rule sets as needed.

type ExpressionRuleCall

type ExpressionRuleCall struct {
	Bindings  *matching.PlannerBindings
	Reference *expressions.Reference
	Context   PlanContext
	// RunContext is the cancellation scope for this planner invocation. It is
	// nil only for standalone rule tests constructed outside Planner.
	RunContext  context.Context
	Constraints *ConstraintMap
	// Stats is the planner's table-cardinality provider, threaded so a
	// rule's internal cost comparisons (e.g. selecting the best physical
	// join child) use REAL cardinalities rather than the default
	// LeafScanCardinality. Nil on test/utility firing paths — CostModel()
	// falls back to the default-stats comparator. Without this, the NLJ
	// rule's findBestValidPhysicalExpr ranks join children with every table at
	// LeafScanCardinality, so child selection ties and commits to the
	// FROM-clause order instead of the cost-optimal one (RFC-041).
	Stats properties.StatisticsProvider
	// contains filtered or unexported fields
}

ExpressionRuleCall is the rule-invocation context used by RelationalExpression-shaped rules. Counterpart to the existing RuleCall (which targets QueryPredicate / Value rules) — split per type so each rule shape gets a strongly-typed Yield.

Ports the consulted surface of Java's `com.apple.foundationdb.record.query.plan.cascades.CascadesRuleCall` (Java's class also covers partial-match yields, planner-phase plumbing, and traversal-state hooks). Go exposes:

  • Bindings: the pattern matcher's results, keyed by matcher identity (already provided by `matching.PlannerBindings`).
  • Reference: the memo group whose member fired the rule. Yields append to this Reference; the dedup happens via Reference.Insert.
  • Context: the PlanContext (planner config + match candidates).
  • Memo: the Memo for cross-Reference memoization (nil when running outside the Planner, e.g. in standalone tests).
  • Yield(expr): insert a new equivalent expression into the Reference.
  • MemoizeExpression(expr): find-or-create a Reference for a sub-expression via the Memo. Falls back to InitialOf when no Memo is present.
  • Yielded(): the list of expressions yielded so far. Tests + the planner's traversal driver consume this.

Java's flavoured yields (exploratory / final / plan / unknown) map onto the single staged Yield here. The task-stack driver publishes the complete batch only after OnMatch succeeds, choosing Insert or InsertFinal by phase; the data-access path routes by physicality via the planner's yieldUnknown.

func NewExpressionRuleCall

func NewExpressionRuleCall(ref *expressions.Reference, bindings *matching.PlannerBindings, ctx PlanContext) *ExpressionRuleCall

NewExpressionRuleCall builds a rule-call against a Reference + an already-computed binding set. Context defaults to EmptyPlanContext if nil — convenient for tests that don't depend on planner config.

func NewExpressionRuleCallWithMemo

func NewExpressionRuleCallWithMemo(ref *expressions.Reference, bindings *matching.PlannerBindings, ctx PlanContext, memo *Memo) *ExpressionRuleCall

NewExpressionRuleCallWithMemo builds a rule-call with a Memo for cross-Reference memoization. Used by the Planner's ApplyRulesTask.

func (*ExpressionRuleCall) CancellationErr

func (c *ExpressionRuleCall) CancellationErr() error

CancellationErr reports whether the owning planning run was canceled. Standalone rule calls with no RunContext are non-cancelable.

func (*ExpressionRuleCall) CommitStagedInserts

func (c *ExpressionRuleCall) CommitStagedInserts()

CommitStagedInserts applies the InsertReExploring calls the rule body staged. Drivers call it only after OnMatch has returned and Err is clear, and BEFORE publishing the yields, so a parent lands over children that are complete.

func (*ExpressionRuleCall) CostModel

func (c *ExpressionRuleCall) CostModel() func(a, b expressions.RelationalExpression) bool

CostModel returns the comparator a rule should use for internal best-plan selection: stats-aware when the planner threaded statistics, else the default-stats comparator.

func (*ExpressionRuleCall) Err

func (c *ExpressionRuleCall) Err() error

Err returns the first failure reported by the rule body.

func (*ExpressionRuleCall) Fail

func (c *ExpressionRuleCall) Fail(err error)

Fail records the first rule-body failure. Drivers must check Err before publishing any staged rule effects.

func (*ExpressionRuleCall) GetRequestedOrderings

func (c *ExpressionRuleCall) GetRequestedOrderings() []*properties.RequestedOrdering

GetRequestedOrderings returns the requested orderings for this Reference from the constraint map, if available. Returns nil if no ordering constraint is set or no constraint map is present.

func (*ExpressionRuleCall) InsertReExploring

func (c *ExpressionRuleCall) InsertReExploring(ref *expressions.Reference, expr expressions.RelationalExpression)

MemoizeExpression finds or creates a Reference for a sub-expression. When a Memo is present (running inside the Planner), this checks if an existing Reference already holds a structurally-equivalent expression and returns it — enabling cross-Reference sharing. Without a Memo (standalone rule testing), falls back to expressions.InitialOf(expr).

The current call's Reference (the one the rule is yielding into) is excluded from reuse to prevent self-referential cycles. This mirrors Java's guard: `Verify.verify(existingReference != this.root)`.

Rules should use this instead of expressions.InitialOf when creating child References for yielded expressions. This is how the Cascades planner avoids redundant exploration of shared sub-trees. InsertReExploring STAGES an insert of expr into ref, to be applied through the memo's scheduled insert (epoch re-arm + re-round when ref's exploration already began) once the driver has checked Err. Rule code adding members to a reference it did not just create must use this instead of Reference.Insert.

It is staged for the same reason Yield is, and the reason is not symmetry: MemoizeExpression can resolve to an EXISTING, already-explored reference (see the note at its call site in DecorrelateValuesRule), so the ref inserted into here may be reachable from the root. A rule that adds a member there and then Fails has published a member it went on to reject — the one effect the staged protocol exists to prevent, escaping through the one call that did not use it.

MemoizeExpression deliberately stays IMMEDIATE. A rule needs the *Reference back to build the parent expression it is about to yield, so deferring it would take a memo journal with rollback; and its residue on failure is an ORPHAN group — created, referenced by no published expression, discarded with the planner. That the planner IS discarded is pinned rather than assumed: Fail sets capErr, the run loop returns on it, and no caller reuses a planner (see TestRuleFailureLeavesNoMemberInALiveGroup).

It returns nothing. The old bool reported the memo's dedup answer, which is not knowable before the commit — the single production caller discarded it, and fabricating one at stage time would be a lie rather than a simplification.

func (*ExpressionRuleCall) MemoizeExpression

func (*ExpressionRuleCall) MemoizeFinalExpression

func (c *ExpressionRuleCall) MemoizeFinalExpression(expr expressions.RelationalExpression) *expressions.Reference

MemoizeFinalExpression creates a NEW Reference holding expr as its single FINAL member, without interning into the shared memo — Java's memoizePlan (Reference.ofFinalExpressions). The ImplementationRuleCall twin.

MemoizeExpression above INTERNS: it asks the memo for a group and may hand back an existing one. That is right for a canonical logical expression, and wrong for a COMPENSATING physical operator a rule just built, which is not equivalent to anything already in the memo and must not be deduped against it.

func (*ExpressionRuleCall) MemoizeMemberPlansFromOther

func (c *ExpressionRuleCall) MemoizeMemberPlansFromOther(
	source *expressions.Reference,
	members []expressions.RelationalExpression,
) *expressions.Reference

MemoizeMemberPlansFromOther mints a NEW reference holding only `members` — which must already be members of `source` — as final expressions. Ports Java's FinalMemoizer.memoizeMemberPlansFromOther (CascadesRuleCall.java:518 → Reference.newReferenceFromFinalMembers:587), whose contract is that the returned reference "is always newly created and never reused".

This is the RESTRICTION an enumerating implementation rule needs, and it is what MemoizeExpression cannot give it. MemoizeExpression INTERNS: asked for a member, it hands back a group that already contains that member — which, for a member of the rule's own child group, is that whole child group. A rule that loops over N child members and memoizes each one therefore builds N structurally IDENTICAL parents over the same reference, and they collapse to one on insert. The loop enumerates nothing.

Java cannot reach that state because it binds a PlanPartition and memoizes a reference restricted to that partition's plans; the restriction is not an optimization there, it is what makes the per-partition parent distinct.

The source's per-plan property map is carried over RESTRICTED to the retained members. Copying it wholesale would leave the new reference reporting partitions (ToPlanPartitions walks the property map, not the member list) for plans it does not contain, which is the same conflation in the other direction.

func (*ExpressionRuleCall) StagedInsertCount

func (c *ExpressionRuleCall) StagedInsertCount() int

StagedInsertCount reports how many inserts are held unpublished. Tests use it to prove staging happened rather than inferring it from the memo being unchanged, which is also what a rule that never inserted would look like.

func (*ExpressionRuleCall) Yield

Yield stages expr in this invocation-local batch. It deliberately does not mutate the Reference or Memo: the driver first lets OnMatch finish, checks Err, validates the complete batch, and only then commits it. The bool reports commit-time deduplication is intentionally not observable from inside the rule body.

func (*ExpressionRuleCall) Yielded

Yielded returns the expressions the rule has yielded so far, including duplicates that Reference.Insert filtered. Useful for rule-firing tests that want to assert on the rule's output without reaching into the Reference's member list.

type ExtractionVerificationReport

type ExtractionVerificationReport struct {
	Violations           []string
	VisitedReferences    int
	DeadEnds             int
	MultiFinalReferences int
	// contains filtered or unexported fields
}

ExtractionVerificationReport describes the extraction-path invariant from RFC-224. VisitedReferences counts distinct canonical References reached by following the member extraction would select. DeadEnds counts reached References for which no physical selection exists. MultiFinalReferences is diagnostic reach: retaining more than one physical final is legal when each retained member is the winner for a required physical property.

Violations contains both dead ends and coherence failures. In particular, a stamped winner must still belong to the Reference's final set, and every retained non-winner in a multi-final group must be licensed by one of the same physical-property partitions OptimizeGroup uses.

func VerifyExtractionIsUnambiguous

func VerifyExtractionIsUnambiguous(
	root *expressions.Reference,
	selector BestMemberSelector,
	stats properties.StatisticsProvider,
) ExtractionVerificationReport

VerifyExtractionIsUnambiguous walks exactly one selected member per reached Reference: a compatible stamped winner first, the selector's compatible best member next, and the cheapest compatible physical member as the fallback. Positional ordinal-layout requirements are derived from the selected parent and applied to the corresponding child edge before cost comparison, exactly as selector-based extraction does.

The cardinality of FinalMembers is deliberately not an invariant. Go keeps one winner per required physical property because parents retain live memo edges. For multi-final groups this verifier instead reconstructs the legal OptimizeGroup keep set: the global winner, the cheapest member of every (distinct-records, stored-record) partition, the cheapest provider of every requested ordering, and the cheapest provider of every required ordinal layout. A survivor outside that set is a coherence violation; a legitimate property-retained alternative is not.

type FilterDedupPredicatesRule

type FilterDedupPredicatesRule struct {
	// contains filtered or unexported fields
}

FilterDedupPredicatesRule removes structurally-duplicate predicates from a LogicalFilter's predicate list.

Filter([P, Q, P], X)  →  Filter([P, Q], X)

SQL semantics: WHERE P AND Q AND P = WHERE P AND Q (idempotent AND). The duplicate adds no row-rejection power.

Soundness via Explain text: two predicates compare equal iff their Explain() rendering matches. Same bridge as the existing `valueNamesEqual` helper in the predicates package.

Termination: yields Filter with deduped list, REUSING the inner Quantifier. Pointer-identity dedup absorbs second fire.

Composes with FilterMergeRule (which can leave duplicates after flattening nested Filters) and AndDedupRule (which dedupes inside an AND predicate at the predicate-tree level — different layer).

func NewFilterDedupPredicatesRule

func NewFilterDedupPredicatesRule() *FilterDedupPredicatesRule

NewFilterDedupPredicatesRule constructs the rule.

func (*FilterDedupPredicatesRule) Matcher

Matcher returns the pattern.

func (*FilterDedupPredicatesRule) OnMatch

OnMatch fires when the predicate list contains at least one Explain-equal duplicate pair.

type FilterDropTruePredicatesRule

type FilterDropTruePredicatesRule struct {
	// contains filtered or unexported fields
}

FilterDropTruePredicatesRule drops TriTrue ConstantPredicates from a LogicalFilter's predicate list (without eliminating the filter itself). Composes naturally with FilterMergeRule (which can leave trivial TRUE conjuncts after merging) and NoOpFilterRule (which then eliminates the filter if ALL predicates are TRUE).

Pattern:

Filter([..., TriTrue-ConstantPredicate, ...]) over X
→
Filter([... without TriTrue ...]) over X

Yields the new Filter only if at least one TriTrue predicate is dropped — otherwise declines.

If dropping leaves the predicate list empty, the rule still fires; NoOpFilterRule will then eliminate the now-trivial Filter on a subsequent exploration round.

func NewFilterDropTruePredicatesRule

func NewFilterDropTruePredicatesRule() *FilterDropTruePredicatesRule

NewFilterDropTruePredicatesRule constructs the rule.

func (*FilterDropTruePredicatesRule) Matcher

Matcher returns the pattern.

func (*FilterDropTruePredicatesRule) OnMatch

OnMatch fires when at least one predicate is TriTrue.

type FilterMergeRule

type FilterMergeRule struct {
	// contains filtered or unexported fields
}

FilterMergeRule consolidates a LogicalFilter whose inner Quantifier ranges over another LogicalFilter into a single LogicalFilter with both predicate lists concatenated. SQL-equivalent: WHERE p1 AND p2 AND p3 ... regardless of whether the parser nested the conjuncts.

Pattern:

LogicalFilter([p_outer...])
  inner: ForEachQuantifier → Reference holding LogicalFilter([p_inner...])
    inner: ForEachQuantifier → Reference holding any RelationalExpression X

Rewrite:

LogicalFilter([p_outer..., p_inner...])
  inner: ForEachQuantifier → Reference holding X

Java equivalent: LogicalFilterMergeRule (or the equivalent SimplifyFilterRule) in `cascades/rules/`.

func NewFilterMergeRule

func NewFilterMergeRule() *FilterMergeRule

NewFilterMergeRule constructs the rule with its pattern matcher pre-built (avoids per-call allocation in the planner driver).

func (*FilterMergeRule) Matcher

Matcher returns the pattern.

func (*FilterMergeRule) OnMatch

func (r *FilterMergeRule) OnMatch(call *ExpressionRuleCall)

OnMatch examines the matched LogicalFilter; if its inner Quantifier's Reference holds another LogicalFilter, yields a single merged LogicalFilter. Otherwise, declines (zero yields).

type FinalizeExpressionsRule

type FinalizeExpressionsRule struct {
	// contains filtered or unexported fields
}

FinalizeExpressionsRule is a REWRITING-phase ImplementationRule that promotes any exploratory expression to a final expression. This is how the REWRITING phase marks its canonical output for OptimizeGroup to select the best, and for advancePlannerStage to promote as the PLANNING seed.

Mirrors Java's FinalizeExpressionsRule.

func NewFinalizeExpressionsRule

func NewFinalizeExpressionsRule() *FinalizeExpressionsRule

func (*FinalizeExpressionsRule) Matcher

func (*FinalizeExpressionsRule) OnMatch

type ForMatchCompensation

type ForMatchCompensation struct {
	// contains filtered or unexported fields
}

ForMatchCompensation is the primary compensation implementation for matches based on query predicates. It tracks matched/unmatched quantifiers, predicate compensation, result compensation, and group-by mappings.

Ports Java's Compensation.ForMatch (which implements Compensation.WithSelectCompensation).

func DerivedCompensation

func DerivedCompensation(
	parent Compensation,
	impossible bool,
	predicateCompensationMap *PredicateCompensationMap,
	matchedQuantifiers []expressions.Quantifier,
	unmatchedQuantifiers []expressions.Quantifier,
	compensatedAliases map[values.CorrelationIdentifier]struct{},
	resultCompensationFn *ResultCompensationFunction,
	groupByMappings *GroupByMappings,
) *ForMatchCompensation

DerivedCompensation creates a new ForMatchCompensation with `parent` as its child compensation. This is the package-level equivalent of Java's Compensation.derived() default method, usable with any Compensation (not just ForMatchCompensation).

func DerivedCompensationWithPrimaryKeyDistinct

func DerivedCompensationWithPrimaryKeyDistinct(
	parent Compensation,
	impossible bool,
	predicateCompensationMap *PredicateCompensationMap,
	matchedQuantifiers []expressions.Quantifier,
	unmatchedQuantifiers []expressions.Quantifier,
	compensatedAliases map[values.CorrelationIdentifier]struct{},
	resultCompensationFn *ResultCompensationFunction,
	groupByMappings *GroupByMappings,
	requiresPrimaryKeyDistinct bool,
) *ForMatchCompensation

DerivedCompensationWithPrimaryKeyDistinct is the explicit cardinality-aware form of DerivedCompensation. Existing callers retain the default false mode.

func NewForMatchCompensation

func NewForMatchCompensation(
	impossible bool,
	childCompensation Compensation,
	predicateCompensationMap *PredicateCompensationMap,
	matchedQuantifiers []expressions.Quantifier,
	unmatchedQuantifiers []expressions.Quantifier,
	compensatedAliases map[values.CorrelationIdentifier]struct{},
	resultCompensationFn *ResultCompensationFunction,
	groupByMappings *GroupByMappings,
) *ForMatchCompensation

NewForMatchCompensation constructs a ForMatchCompensation.

Mirrors Java's Compensation.ForMatch constructor. All collection fields are defensively copied.

func NewForMatchCompensationWithPrimaryKeyDistinct

func NewForMatchCompensationWithPrimaryKeyDistinct(
	impossible bool,
	childCompensation Compensation,
	predicateCompensationMap *PredicateCompensationMap,
	matchedQuantifiers []expressions.Quantifier,
	unmatchedQuantifiers []expressions.Quantifier,
	compensatedAliases map[values.CorrelationIdentifier]struct{},
	resultCompensationFn *ResultCompensationFunction,
	groupByMappings *GroupByMappings,
	requiresPrimaryKeyDistinct bool,
) *ForMatchCompensation

NewForMatchCompensationWithPrimaryKeyDistinct constructs a compensation that can additionally require primary-key cardinality correction. The ordinary constructor remains source-compatible and defaults this obligation to false.

func (*ForMatchCompensation) Apply

Apply applies this compensation to a relational expression by wrapping it with residual predicate filters. It returns false when the compensation cannot be applied faithfully; callers must discard that match.

translationMapFunc, given the realized base-quantifier alias, yields a TranslationMap that rebases compensated predicates from the candidate's top alias to the realized alias. The realized base quantifier is created with the matched query-side ForEach alias (matchedForEachAlias) — exactly as Java does — so the compensation expression flows under the SAME alias the surrounding query graph already correlates to. Allocating a fresh alias here instead orphans the access from outer correlations (the outer join's reference to the matched source no longer resolves), which is how a dual-correlation join collapsed to Fetch(<nil>) and returned 0 rows.

Ports Java's Compensation.ForMatch.apply — full implementation including the else-branch (multi-join compensation with unmatched ForEach quantifiers pulled up into a new SelectExpression).

func (*ForMatchCompensation) ApplyAllNeeded

ApplyAllNeeded applies both filter compensation (Apply) and result compensation (ApplyFinal) as needed. This is the primary entry point for applying compensation to a plan expression; false makes the data-access alternative non-viable.

Ports Java's Compensation.applyAllNeededCompensations.

func (*ForMatchCompensation) ApplyFinal

ApplyFinal applies the result (shape) compensation by wrapping the expression in a SelectExpression with the translated result value. It returns false when a needed final compensation cannot be applied faithfully.

Ports Java's Compensation.WithSelectCompensation.applyFinal which uses GraphExpansion.builder().addQuantifier(base).build() .buildSelectWithResultValue(resultValue).

func (*ForMatchCompensation) CanBeDeferred

func (c *ForMatchCompensation) CanBeDeferred() bool

CanBeDeferred reports whether this compensation can be combined with subsequent compensations further up the graph. Mirrors Java's Compensation.canBeDeferred() default (always returns true).

func (*ForMatchCompensation) Derived

func (c *ForMatchCompensation) Derived(
	impossible bool,
	predicateCompensationMap *PredicateCompensationMap,
	matchedQuantifiers []expressions.Quantifier,
	unmatchedQuantifiers []expressions.Quantifier,
	compensatedAliases map[values.CorrelationIdentifier]struct{},
	resultCompensationFn *ResultCompensationFunction,
	groupByMappings *GroupByMappings,
) *ForMatchCompensation

Derived creates a new ForMatchCompensation with this compensation as its child. This mirrors Java's Compensation.derived() default method.

func (*ForMatchCompensation) DerivedWithPrimaryKeyDistinct

func (c *ForMatchCompensation) DerivedWithPrimaryKeyDistinct(
	impossible bool,
	predicateCompensationMap *PredicateCompensationMap,
	matchedQuantifiers []expressions.Quantifier,
	unmatchedQuantifiers []expressions.Quantifier,
	compensatedAliases map[values.CorrelationIdentifier]struct{},
	resultCompensationFn *ResultCompensationFunction,
	groupByMappings *GroupByMappings,
	requiresPrimaryKeyDistinct bool,
) *ForMatchCompensation

DerivedWithPrimaryKeyDistinct creates a derived compensation and explicitly records whether this new match level requires primary-key distinct.

func (*ForMatchCompensation) GetChildCompensation

func (c *ForMatchCompensation) GetChildCompensation() Compensation

GetChildCompensation returns the child (inner) compensation.

func (*ForMatchCompensation) GetCompensatedAliases

func (c *ForMatchCompensation) GetCompensatedAliases() map[values.CorrelationIdentifier]struct{}

GetCompensatedAliases returns the set of aliases this compensation is responsible for. When applied, the caller can be assured that the match replacement plus this compensation can replace the quantifiers identified by these aliases.

func (*ForMatchCompensation) GetGroupByMappings

func (c *ForMatchCompensation) GetGroupByMappings() *GroupByMappings

GetGroupByMappings returns the group-by mappings.

func (*ForMatchCompensation) GetMatchedQuantifiers

func (c *ForMatchCompensation) GetMatchedQuantifiers() []expressions.Quantifier

GetMatchedQuantifiers returns the set of quantifiers that were matched during matching.

func (*ForMatchCompensation) GetPredicateCompensationMap

func (c *ForMatchCompensation) GetPredicateCompensationMap() *PredicateCompensationMap

GetPredicateCompensationMap returns the predicate compensation map.

func (*ForMatchCompensation) GetResultCompensationFunction

func (c *ForMatchCompensation) GetResultCompensationFunction() *ResultCompensationFunction

GetResultCompensationFunction returns the result compensation function.

func (*ForMatchCompensation) GetUnmatchedForEachQuantifiers

func (c *ForMatchCompensation) GetUnmatchedForEachQuantifiers() []expressions.Quantifier

GetUnmatchedForEachQuantifiers returns the subset of unmatched quantifiers that are ForEach quantifiers. The result is lazily computed and cached.

Mirrors Java's ForMatch.computeUnmatchedForEachQuantifiers() with Suppliers.memoize.

func (*ForMatchCompensation) GetUnmatchedQuantifiers

func (c *ForMatchCompensation) GetUnmatchedQuantifiers() []expressions.Quantifier

GetUnmatchedQuantifiers returns the set of quantifiers that were NOT matched during matching.

func (*ForMatchCompensation) Intersect

Intersect combines this compensation with another by keeping only predicates that appear in both (common residuals for index intersections). Returns ImpossibleCompensation if the intersection is infeasible.

Ports Java's Compensation.WithSelectCompensation.intersect.

func (*ForMatchCompensation) IsFinalNeeded

func (c *ForMatchCompensation) IsFinalNeeded() bool

IsFinalNeeded reports whether final result-shape compensation is needed. Mirrors Java's WithSelectCompensation.isFinalNeeded() default method.

func (*ForMatchCompensation) IsImpossible

func (c *ForMatchCompensation) IsImpossible() bool

IsImpossible reports whether this compensation is infeasible.

func (*ForMatchCompensation) IsNeeded

func (c *ForMatchCompensation) IsNeeded() bool

IsNeeded reports whether this compensation must be applied. Mirrors Java's WithSelectCompensation.isNeeded() default method.

func (*ForMatchCompensation) IsNeededForFiltering

func (c *ForMatchCompensation) IsNeededForFiltering() bool

IsNeededForFiltering reports whether this compensation needs to be applied for correct filtering. Mirrors Java's WithSelectCompensation.isNeededForFiltering() default method.

func (*ForMatchCompensation) MatchedForEachAliasMaybe

func (c *ForMatchCompensation) MatchedForEachAliasMaybe() (values.CorrelationIdentifier, bool)

MatchedForEachAliasMaybe returns the single base ForEach alias this compensation rebuilds itself on top of, and whether that alias is well-defined.

Applying a compensation means re-introducing a base quantifier over the compensated expression and re-pointing the residual predicates and result value at it. That has an answer only when exactly one matched quantifier is a ForEach supplying the rows: with none there is no alias to rebuild on, with several there is no way to choose and the losers' correlations dangle. Java asserts this with Iterables.getOnlyElement and crashes; Go reports it so callers can drop the match instead of building on a guessed alias.

The compensated aliases must also be exactly the matched ones. That pair of sets is what the compensation claims responsibility for; when they disagree the compensation is describing a different set of quantifiers than the one it is about to rebuild, and composing it (intersect, union) propagates the disagreement. Java verifies the equality before selecting the sole ForEach for the same reason.

func (*ForMatchCompensation) RequiresPrimaryKeyDistinct

func (c *ForMatchCompensation) RequiresPrimaryKeyDistinct() bool

RequiresPrimaryKeyDistinct reports whether this match level must correct duplicate cardinality by primary key before applying its final result projection. It deliberately does not contribute to IsNeededForFiltering: duplicate elimination changes multiplicity, not the truth of an existential predicate.

func (*ForMatchCompensation) String

func (c *ForMatchCompensation) String() string

String returns a human-readable representation of this compensation. Mirrors Java's ForMatch.toString().

func (*ForMatchCompensation) Union

Union combines this compensation with another by merging predicate maps from both sides. Used when multiple partial matches combine their compensations (e.g. union of data access matches).

Ports Java's Compensation.WithSelectCompensation.union.

type GraphExpansion

type GraphExpansion struct {
	// contains filtered or unexported fields
}

GraphExpansion accumulates index-expansion components (result columns, predicates, quantifiers, placeholders) before they are sealed and built into a SelectExpression. Mirrors Java's `com.apple.foundationdb.record.query.plan.cascades.GraphExpansion`.

Think of it as a builder for SelectExpression that also supports merging multiple expansions and inspecting intermediate state. The fundamental difference from a regular builder is that it exposes getters and additive merge semantics.

func EmptyGraphExpansion

func EmptyGraphExpansion() *GraphExpansion

EmptyGraphExpansion returns a zero-valued expansion with all slices initialized (non-nil but empty). Equivalent to Java's `GraphExpansion.empty()`.

func MergeGraphExpansions

func MergeGraphExpansions(expansions ...*GraphExpansion) *GraphExpansion

MergeGraphExpansions combines multiple expansions by concatenating their result columns, predicates, quantifiers, and placeholders. Equivalent to Java's `GraphExpansion.ofOthers(...)`.

func NewGraphExpansion

func NewGraphExpansion(
	columns []GraphExpansionColumn,
	preds []predicates.QueryPredicate,
	quants []expressions.Quantifier,
	phs []*predicates.Placeholder,
) *GraphExpansion

NewGraphExpansion constructs a GraphExpansion from explicit slices. All slices are defensively copied. Equivalent to Java's `GraphExpansion.of(...)`.

func (*GraphExpansion) GetPlaceholders

func (g *GraphExpansion) GetPlaceholders() []*predicates.Placeholder

GetPlaceholders returns the placeholder list.

func (*GraphExpansion) GetPredicates

func (g *GraphExpansion) GetPredicates() []predicates.QueryPredicate

GetPredicates returns the predicate list.

func (*GraphExpansion) GetQuantifiers

func (g *GraphExpansion) GetQuantifiers() []expressions.Quantifier

GetQuantifiers returns the quantifier list.

func (*GraphExpansion) GetResultColumns

func (g *GraphExpansion) GetResultColumns() []GraphExpansionColumn

GetResultColumns returns the result column list.

func (*GraphExpansion) Seal

Seal deduplicates placeholders and returns an immutable SealedGraphExpansion. Mirrors Java's `GraphExpansion.seal()`.

Placeholder deduplication:

  1. Group by ParameterAlias — placeholders with the same alias are collapsed (ranges merged).
  2. Among the unique-by-alias placeholders, identify those whose Values are semantically equal (by structural comparison). Replace all duplicates-by-value with the first representative.

The resulting SealedGraphExpansion carries deduplicated predicates, the original quantifiers, deduplicated placeholders, and a result value built from the columns.

type GraphExpansionBuilder

type GraphExpansionBuilder struct {
	// contains filtered or unexported fields
}

GraphExpansionBuilder accumulates graph expansion components incrementally. Mirrors Java's `GraphExpansion.Builder`.

func NewGraphExpansionBuilder

func NewGraphExpansionBuilder() *GraphExpansionBuilder

NewGraphExpansionBuilder returns an empty builder.

func (*GraphExpansionBuilder) AddColumn

func (b *GraphExpansionBuilder) AddColumn(name string, value values.Value) *GraphExpansionBuilder

AddColumn appends a named result column.

func (*GraphExpansionBuilder) AddPlaceholder

AddPlaceholder appends a placeholder.

func (*GraphExpansionBuilder) AddPredicate

AddPredicate appends a predicate.

func (*GraphExpansionBuilder) AddQuantifier

AddQuantifier appends a quantifier.

func (*GraphExpansionBuilder) Build

Build produces a GraphExpansion from the accumulated state.

type GraphExpansionColumn

type GraphExpansionColumn struct {
	Name  string
	Value values.Value
}

GraphExpansionColumn pairs a column name with the Value that computes its contents. Mirrors Java's Column<? extends Value> in the context of GraphExpansion — simplified since Go doesn't have Java's typed Field wrapper. An empty Name represents an unnamed/anonymous column.

type GroupByMappings

type GroupByMappings struct {
	// contains filtered or unexported fields
}

GroupByMappings tracks matched groupings, matched aggregates, and unmatched aggregates during aggregate index matching. It is a data holder with immutable semantics — all maps are defensively copied at construction time.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.GroupByMappings.

Fields:

  • matchedGroupingsMap: query grouping Value -> candidate grouping Value (bijective)
  • matchedAggregatesMap: query aggregate Value -> candidate aggregate Value (bijective)
  • unmatchedAggregatesMap: CorrelationIdentifier -> query aggregate Value not yet matched (bijective)

func AdjustGroupByMappings

func AdjustGroupByMappings(
	gbm *GroupByMappings,
	candidateAlias values.CorrelationIdentifier,
	candidateExpression expressions.RelationalExpression,
) (*GroupByMappings, bool)

AdjustGroupByMappings pulls up the GroupByMappings through a candidate expression. For each matched grouping/aggregate value pair, pulls up the candidate-side value through the candidate expression's result value.

Ports Java's MatchInfo.adjustGroupByMappings.

func EmptyGroupByMappings

func EmptyGroupByMappings() *GroupByMappings

EmptyGroupByMappings returns a GroupByMappings with all three maps empty. Mirrors Java's GroupByMappings.empty().

func NewGroupByMappings

func NewGroupByMappings(
	matchedGroupingsMap *BiMap[values.Value, values.Value],
	matchedAggregatesMap *BiMap[values.Value, values.Value],
	unmatchedAggregatesMap *BiMap[values.CorrelationIdentifier, values.Value],
) *GroupByMappings

NewGroupByMappings creates a GroupByMappings from the three maps. All maps are defensively copied so mutations to the originals don't affect this instance. Mirrors Java's GroupByMappings.of().

func (*GroupByMappings) MatchedAggregatesMap

func (g *GroupByMappings) MatchedAggregatesMap() *BiMap[values.Value, values.Value]

MatchedAggregatesMap returns the bidirectional map from query aggregate Values to candidate aggregate Values.

func (*GroupByMappings) MatchedGroupingsMap

func (g *GroupByMappings) MatchedGroupingsMap() *BiMap[values.Value, values.Value]

MatchedGroupingsMap returns the bidirectional map from query grouping Values to candidate grouping Values.

func (*GroupByMappings) UnmatchedAggregatesMap

func (g *GroupByMappings) UnmatchedAggregatesMap() *BiMap[values.CorrelationIdentifier, values.Value]

UnmatchedAggregatesMap returns the bidirectional map from CorrelationIdentifiers to query aggregate Values that don't have a candidate match.

type ImplementDeleteRule

type ImplementDeleteRule struct {
	// contains filtered or unexported fields
}

ImplementDeleteRule implements a logical DeleteExpression as a physical RecordQueryDeletePlan.

Delete(target, stored-record-access-path)
  →  DeletePlan(target,
         UnorderedPrimaryKeyDistinct(stored-record-access-path))

Ports Java's ImplementDeleteRule 1:1 on both of its structural decisions:

  • the inner reference is bound only through partitions whose StoredRecordProperty holds (ImplementDeleteRule.java:55-57), and
  • a primary-key dedup is interposed between the access path and the mutation UNLESS the access path already proves DistinctRecordsProperty.distinctRecords() (ImplementDeleteRule.java:79-82).

The short-circuit is the ONE place the two DML rules differ: an UPDATE gets the dedup unconditionally (ImplementUpdateRule.java:79-80). Reading that as an inconsistency and unifying it either way would be wrong in one direction or the other, so it is preserved exactly.

func NewImplementDeleteRule

func NewImplementDeleteRule() *ImplementDeleteRule

NewImplementDeleteRule constructs the rule.

func (*ImplementDeleteRule) Matcher

Matcher returns the pattern.

func (*ImplementDeleteRule) OnMatch

func (r *ImplementDeleteRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires on every DeleteExpression, once per stored-record access path the inner reference offers.

type ImplementDistinctFinalRule

type ImplementDistinctFinalRule struct {
	// contains filtered or unexported fields
}

Go extension: Java's fdb-relational 4.11.1.0 rejects SELECT DISTINCT for most query shapes; Go supports it broadly via this rule and the hash-distinct executor.

ImplementDistinctFinalRule is the PLANNING-phase rule for LogicalDistinctExpression. Ports Java's ImplementDistinctRule (ImplementationCascadesRule<LogicalDistinctExpression>).

For each physical FinalMember, the rule checks two sources of distinctness information:

  1. Physical-level DistinctRecordsProperty — per-member, matching Java's ImplementDistinctRule 1:1. A scan on a unique index, an identity-mapping MapPlan, etc. propagate distinctness through the property system.
  2. Logical-level PRIMARY KEY column coverage — Go extension, fallback. If the projected columns cover a primary key, ALL physical plans are guaranteed distinct (equivalent to Java's "strictlySorted" path where all partition members get elided). Primary-key uniqueness is a storage invariant, so this license is unconditional.
  3. Logical-level SECONDARY UNIQUE index coverage — see secondaryUniqueEliminationProof. Unlike 1 and 2 this license rests on MUTABLE STORE STATE, so the yielded plan carries the proving index's name as a stamp and the index becomes a plan dependency revalidated in every execution transaction.

The order matters and is enforced in yieldElidedOrWrapped: 1, then 2, then 3, and ONLY 3 stamps. An elision the property path or the primary key already justified must not record a dependency its correctness does not rest on.

When any check passes, the Distinct operator is elided and the inner plan is yielded directly. Otherwise, the inner is wrapped with RecordQueryDistinctPlan.

This rule subsumes the former DistinctOnUniqueElimRule (which ran during EXPLORE). Moving the elimination check to PLANNING matches Java's architecture: ImplementDistinctRule is an ImplementationCascadesRule, not an exploration rule.

func NewImplementDistinctFinalRule

func NewImplementDistinctFinalRule() *ImplementDistinctFinalRule

func (*ImplementDistinctFinalRule) Matcher

func (*ImplementDistinctFinalRule) OnMatch

type ImplementDistinctUnionRule

type ImplementDistinctUnionRule struct {
	// contains filtered or unexported fields
}

ImplementDistinctUnionRule implements Distinct(Union(legs...)) as a merge-sorted union plan. It matches LogicalDistinctExpression over LogicalUnionExpression, finds compatible orderings across all union legs, and creates a RecordQueryMergeSortUnionPlan with deduplication.

Ports Java's ImplementDistinctUnionRule. The full algorithm:

  1. Get requested orderings from planner constraints
  2. For each cross-product combo of per-leg plan partitions: a. Verify common primary key across all legs b. Extract ordering from each leg's partition c. Incrementally merge orderings (bail on incompatibility) d. Verify PK values are covered by merged ordering e. Enumerate comparison keys satisfying the requested ordering f. Create MergeSortUnionPlan with comparison keys

func NewImplementDistinctUnionRule

func NewImplementDistinctUnionRule() *ImplementDistinctUnionRule

NOTE: this matches LogicalDistinctExpression, the FULL-ROW dedup node, while the OR-to-union rewrite now emits LogicalUniqueExpression — PK dedup, Java's meaning. The ordered union alternative is therefore unreachable from the OR path. Measured as already dead before that change (0 MergeSortUnion in the plan-shape golden on BOTH sides), so nothing regressed — which is exactly why it would otherwise go unnoticed. Tracked in TODO.md, "The ordered OR-union alternative is structurally unreachable".

func (*ImplementDistinctUnionRule) Matcher

func (*ImplementDistinctUnionRule) OnMatch

type ImplementExplodeRule

type ImplementExplodeRule struct {
	// contains filtered or unexported fields
}

ImplementExplodeRule converts an ExplodeExpression into a physical RecordQueryExplodePlan. Direct translation — the collection Value passes through unchanged.

Mirrors Java's ImplementExplodeRule.

func NewImplementExplodeRule

func NewImplementExplodeRule() *ImplementExplodeRule

func (*ImplementExplodeRule) Matcher

func (*ImplementExplodeRule) OnMatch

func (r *ImplementExplodeRule) OnMatch(call *ExpressionRuleCall)

type ImplementFilterRule

type ImplementFilterRule struct {
	// contains filtered or unexported fields
}

ImplementFilterRule implements a logical LogicalFilterExpression as a physical RecordQueryPredicatesFilterPlan, provided the inner Quantifier's Reference contains at least one physical-plan member.

Filter([P], inner-with-physical-member)
  →  PredicatesFilterPlan([P], inner-physical, innerAlias)

Matches Java's ImplementFilterRule which creates RecordQueryPredicatesFilterPlan with a physical quantifier morphed from the logical inner quantifier, preserving the alias. The alias is critical for correlated predicates: the executor binds the current row under innerAlias so QOV-based predicates can resolve.

The "with-physical-member" guard ensures the rule fires only AFTER the inner has been implemented (i.e. PrimaryScanRule or another implement rule has yielded a physical wrapper into the inner's Reference). This avoids producing partial physical plans that reference still-logical inner trees.

func NewImplementFilterRule

func NewImplementFilterRule() *ImplementFilterRule

NewImplementFilterRule constructs the rule.

func (*ImplementFilterRule) Matcher

Matcher returns the pattern.

func (*ImplementFilterRule) OnMatch

func (r *ImplementFilterRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires on every LogicalFilterExpression. Walks the inner Reference for any physical-plan member; if found, yields a FilterPlan wrapper over a fresh Reference holding that physical inner.

type ImplementInJoinRule

type ImplementInJoinRule struct {
	// contains filtered or unexported fields
}

ImplementInJoinRule implements a SELECT over ExplodeExpressions (UNNEST of IN-lists) and a correlated inner plan as a right-deep chain of RecordQueryInJoinPlans.

Ports Java's ImplementInJoinRule. The rule examines the inner plan's RichOrdering to match explode aliases to equality-bound ordering keys. For each FixedBinding in the ordering, the comparison's GetCorrelatedTo() identifies the explode alias. Matched explodes become sorted IN-sources placed outermost in the InJoin chain, exploiting the inner plan's index ordering. Unmatched explodes use default (unsorted) quantifier order.

func NewImplementInJoinRule

func NewImplementInJoinRule() *ImplementInJoinRule

func (*ImplementInJoinRule) Matcher

func (*ImplementInJoinRule) OnMatch

func (r *ImplementInJoinRule) OnMatch(call *ImplementationRuleCall)

type ImplementInMemorySortRule

type ImplementInMemorySortRule struct {
	// contains filtered or unexported fields
}

ImplementInMemorySortRule yields a RecordQueryInMemorySortPlan for any LogicalSortExpression whose inner Reference has a physical plan. Unlike ImplementSortRule (Java-ported), this does NOT check whether the inner ordering already satisfies the sort — it unconditionally wraps. The cost model ensures this plan loses to index-based elimination when both are available.

func NewImplementInMemorySortRule

func NewImplementInMemorySortRule() *ImplementInMemorySortRule

func (*ImplementInMemorySortRule) GetRequestedOrderings

func (*ImplementInMemorySortRule) Matcher

func (*ImplementInMemorySortRule) OnMatch

type ImplementInUnionRule

type ImplementInUnionRule struct {
	// contains filtered or unexported fields
}

ImplementInUnionRule implements a SELECT over ExplodeExpressions as a RecordQueryInUnionPlan — the inner plan is executed once per IN value and results are merge-sorted by comparison keys.

Ports Java's ImplementInUnionRule. The rule adjusts the inner plan's ordering bindings: fixed bindings referencing explode aliases are promoted to directional (sorted) bindings, enabling merge-sorted output. Comparison keys are derived from the adjusted ordering.

func NewImplementInUnionRule

func NewImplementInUnionRule() *ImplementInUnionRule

func (*ImplementInUnionRule) Matcher

func (*ImplementInUnionRule) OnMatch

func (r *ImplementInUnionRule) OnMatch(call *ImplementationRuleCall)

type ImplementInsertRule

type ImplementInsertRule struct {
	// contains filtered or unexported fields
}

ImplementInsertRule implements a logical InsertExpression as a physical RecordQueryInsertPlan, gated on the inner Reference having at least one physical-plan member.

Insert(target, type, inner-with-physical-member)
  →  InsertPlan(target, type, inner-physical)

Same gating pattern as Implement{Filter,Sort,Distinct,TypeFilter}.

Java's ImplementInsertRule consults PlanPartition properties for dispatch; Go always emits the simple INSERT plan. Per-row transforms (UPSERT, ON CONFLICT, etc.) are not implemented.

func NewImplementInsertRule

func NewImplementInsertRule() *ImplementInsertRule

NewImplementInsertRule constructs the rule.

func (*ImplementInsertRule) Matcher

Matcher returns the pattern.

func (*ImplementInsertRule) OnMatch

func (r *ImplementInsertRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires on every InsertExpression with a physical inner.

type ImplementIntersectionRule

type ImplementIntersectionRule struct {
	// contains filtered or unexported fields
}

ImplementIntersectionRule implements a logical LogicalIntersectionExpression as a physical RecordQueryIntersectionPlan, gated on EVERY child Reference having an ascending physical-plan member for the comparison key.

Intersection(child0-ordered-by-key, child1-ordered-by-key, ...)
  →  IntersectionPlan(child0-ordered-by-key, child1-ordered-by-key, ...)

RecordQueryIntersectionPlan is a sorted merge: merely finding an arbitrary physical member for each leg is not sufficient. A leg that does not emit the comparison key monotonically can make the merge permanently advance past a real match. This rule therefore pins one ordering-satisfying physical spine per child and declines when any leg cannot provide it.

LogicalIntersectionExpression currently carries comparison values but no direction, so this implementation is the natural forward/ascending variant. The data-access intersector builds directional/reverse variants directly.

Java has multiple Intersection variants (ordered, unordered, primary-key-based, value-based); this rule always emits the generic RecordQueryIntersectionPlan (the primary-key-keyed cross-candidate intersection is built separately by the data-access path — WithPrimaryKeyIntersector, planner.go).

func NewImplementIntersectionRule

func NewImplementIntersectionRule() *ImplementIntersectionRule

NewImplementIntersectionRule constructs the rule.

func (*ImplementIntersectionRule) Matcher

Matcher returns the pattern.

func (*ImplementIntersectionRule) OnMatch

OnMatch fires when EVERY child Quantifier ranges over a Reference with an ascending physical-plan member for the comparison key.

type ImplementLimitRule

type ImplementLimitRule struct {
	// contains filtered or unexported fields
}

ImplementLimitRule converts a LogicalLimitExpression into a physical RecordQueryLimitPlan. LIMIT/OFFSET is a pure pass-through that caps the row count — it applies to whatever physical plan the inner produces.

Go-only extension: Java doesn't support LIMIT in SQL; it uses ExecuteProperties.setReturnedRowLimit() at the JDBC layer.

func NewImplementLimitRule

func NewImplementLimitRule() *ImplementLimitRule

func (*ImplementLimitRule) Matcher

func (*ImplementLimitRule) OnMatch

func (r *ImplementLimitRule) OnMatch(call *ExpressionRuleCall)

type ImplementNestedLoopJoinRule

type ImplementNestedLoopJoinRule struct {
	// contains filtered or unexported fields
}

ImplementNestedLoopJoinRule implements a SelectExpression with exactly 2 quantifiers (a binary join) as a physical nested-loop join plan. The left (first) quantifier becomes the outer and the right (second) becomes the inner.

Select(predicates, [Q_left, Q_right])
  → NestedLoopJoin(outer=physical(Q_left), inner=physical(Q_right), predicates)

This is the simplest and most general join implementation — it works for all join shapes without requiring sorted input or hash tables. Cost model: O(N_outer × N_inner) with predicate filtering.

Mirrors Java's `ImplementNestedLoopJoinRule`.

func NewImplementNestedLoopJoinRule

func NewImplementNestedLoopJoinRule() *ImplementNestedLoopJoinRule

func (*ImplementNestedLoopJoinRule) Matcher

func (*ImplementNestedLoopJoinRule) OnMatch

type ImplementProjectionFinalRule

type ImplementProjectionFinalRule struct {
	// contains filtered or unexported fields
}

ImplementProjectionFinalRule is the PLANNING-phase counterpart of ImplementProjectionRule. It fires after the inner has been finalized (children are physical plans), producing a physical projection wrapper.

func NewImplementProjectionFinalRule

func NewImplementProjectionFinalRule() *ImplementProjectionFinalRule

func (*ImplementProjectionFinalRule) Matcher

func (*ImplementProjectionFinalRule) OnMatch

type ImplementProjectionRule

type ImplementProjectionRule struct {
	// contains filtered or unexported fields
}

ImplementProjectionRule implements a logical LogicalProjectionExpression as a physical RecordQueryProjectionPlan, gated on the inner Reference having at least one physical-plan member.

func NewImplementProjectionRule

func NewImplementProjectionRule() *ImplementProjectionRule

func (*ImplementProjectionRule) Matcher

func (*ImplementProjectionRule) OnMatch

func (r *ImplementProjectionRule) OnMatch(call *ExpressionRuleCall)

type ImplementRecursiveDfsJoinRule

type ImplementRecursiveDfsJoinRule struct {
	// contains filtered or unexported fields
}

ImplementRecursiveDfsJoinRule converts a RecursiveUnionExpression (where DFS traversal is allowed) into a physical RecordQueryRecursiveDfsJoinPlan.

Pattern:

RecursiveUnion(initial_state, recursive_state)
  where dfsAllowed()
  → RecursiveDfsJoin(physical(initial), physical(recursive), priorCorrelation, strategy)

The initial-state leg must already have a physical plan (yielded by prior TempTableInsert → inner plan implement rules). The recursive leg similarly must have a physical plan available.

Mirrors Java's ImplementRecursiveDfsJoinRule.

func NewImplementRecursiveDfsJoinRule

func NewImplementRecursiveDfsJoinRule() *ImplementRecursiveDfsJoinRule

func (*ImplementRecursiveDfsJoinRule) Matcher

func (*ImplementRecursiveDfsJoinRule) OnMatch

type ImplementRecursiveLevelUnionRule

type ImplementRecursiveLevelUnionRule struct {
	// contains filtered or unexported fields
}

ImplementRecursiveLevelUnionRule converts a RecursiveUnionExpression (where level-order traversal is allowed) into a physical RecordQueryRecursiveLevelUnionPlan.

Pattern:

RecursiveUnion(initial_state, recursive_state)
  where levelAllowed()
  → RecursiveLevelUnion(physical(initial), physical(recursive), scanAlias, insertAlias)

Both legs must already have physical plans available (yielded by prior TempTableInsert → inner plan implement rules).

Mirrors Java's ImplementRecursiveLevelUnionRule.

func NewImplementRecursiveLevelUnionRule

func NewImplementRecursiveLevelUnionRule() *ImplementRecursiveLevelUnionRule

func (*ImplementRecursiveLevelUnionRule) Matcher

func (*ImplementRecursiveLevelUnionRule) OnMatch

type ImplementSimpleSelectRule

type ImplementSimpleSelectRule struct {
	// contains filtered or unexported fields
}

ImplementSimpleSelectRule implements a SelectExpression with a single quantifier (no joins) as a combination of physical plans:

  • RecordQueryPredicatesFilterPlan for WHERE predicates
  • RecordQueryMapPlan for non-trivial result values (projections)

Ports Java's ImplementSimpleSelectRule. Only fires on single-quantifier SELECTs; multi-quantifier SELECTs (joins) are handled by other rules.

func NewImplementSimpleSelectRule

func NewImplementSimpleSelectRule() *ImplementSimpleSelectRule

func (*ImplementSimpleSelectRule) Matcher

func (*ImplementSimpleSelectRule) OnMatch

type ImplementSortRule

type ImplementSortRule struct {
	// contains filtered or unexported fields
}

ImplementSortRule removes a logical LogicalSortExpression when the inner plan already satisfies the requested ordering. This is Java's RemoveSortRule pattern: sort is a constraint, not a physical operator.

During PLANNING's top-down pass, the sort expression's requested ordering is pushed as a constraint to the inner reference (via GetRequestedOrderings). During the bottom-up pass, this rule checks if the inner partition's ordering satisfies the request, and if so, yields the inner plans directly (removing the sort).

Ports Java's RemoveSortRule (ImplementationCascadesRule).

func NewImplementSortRule

func NewImplementSortRule() *ImplementSortRule

func (*ImplementSortRule) GetRequestedOrderings

func (r *ImplementSortRule) GetRequestedOrderings(
	expr expressions.RelationalExpression,
) []*properties.RequestedOrdering

func (*ImplementSortRule) Matcher

func (*ImplementSortRule) OnMatch

func (r *ImplementSortRule) OnMatch(call *ImplementationRuleCall)

type ImplementStreamingAggregationRule

type ImplementStreamingAggregationRule struct {
	// contains filtered or unexported fields
}

ImplementStreamingAggregationRule implements a GroupByExpression as a physical RecordQueryStreamingAggregationPlan.

GroupBy(keys=[k1, k2], aggs=[...], inner)
  → StreamingAggPlan(inner-physical)   [when inner ordered by k1, k2, ...]

The streaming aggregation is the cheapest aggregation strategy when the input is already sorted — it processes rows in one pass with O(1) memory per group.

TWO PRECONDITIONS on the inner, and Java enforces both (ImplementStreamingAggregationRule.java:68-78 filters the plan partitions on the second before consulting the first at :118-119):

  1. ORDERING. The inner must deliver equal grouping keys ADJACENTLY, or a group is split across runs and aggregated twice. Where no inner provides it, this rule inserts an in-memory sort (a Go extension — Java refuses GROUP BY without sorted input).

  2. CONTINUABLE WITHOUT DUPLICATES. The inner must never re-emit, across a continuation, a row it already emitted. An aggregate FOLDS each row into an accumulator, so a row delivered twice is counted twice and COUNT/SUM/AVG come out wrong — this is the one consumer for which a re-emitted row is incorrect rather than merely redundant. Checked by plans.EvaluateContinuableWithoutDuplicates.

Precondition 2's false set is EMPTY in Go, so its filter currently admits everything — see the property for the audit and for what would re-arm it. The guard is kept because it is correct-by-construction: it is the wrong thing to add reactively, after a cursor that re-emits has already shipped with an aggregate silently over-counting on top of it.

IF THE FALSE SET EVER BECOMES NON-EMPTY, THIS RULE IS NOT ENOUGH ON ITS OWN. Go has no hash-aggregation rule or plan; streaming aggregation is the only aggregation strategy, and the in-memory-sort path below is a sort, not a fallback — a sort over a re-emitting inner buffers the duplicates, so the property (default-from-children) declines that path too. Declining the only path means GROUP BY over the offending shape FAILS TO PLAN rather than falling back. A hash aggregation has to land before anything is added to the false set.

Java equivalent: ImplementStreamingAggregationRule in the OPTIMIZE phase.

func NewImplementStreamingAggregationRule

func NewImplementStreamingAggregationRule() *ImplementStreamingAggregationRule

func (*ImplementStreamingAggregationRule) Matcher

func (*ImplementStreamingAggregationRule) OnMatch

type ImplementTableFunctionRule

type ImplementTableFunctionRule struct {
	// contains filtered or unexported fields
}

ImplementTableFunctionRule converts a TableFunctionExpression into a physical RecordQueryTableFunctionPlan. Direct translation — the streaming Value passes through unchanged.

Mirrors Java's ImplementTableFunctionRule.

func NewImplementTableFunctionRule

func NewImplementTableFunctionRule() *ImplementTableFunctionRule

func (*ImplementTableFunctionRule) Matcher

func (*ImplementTableFunctionRule) OnMatch

type ImplementTempTableInsertRule

type ImplementTempTableInsertRule struct {
	// contains filtered or unexported fields
}

ImplementTempTableInsertRule converts a TempTableInsertExpression to a physical TempTableInsertPlan. Requires the inner reference to already contain a physical plan. Mirrors Java's ImplementTempTableInsertRule.

func NewImplementTempTableInsertRule

func NewImplementTempTableInsertRule() *ImplementTempTableInsertRule

func (*ImplementTempTableInsertRule) Matcher

func (*ImplementTempTableInsertRule) OnMatch

type ImplementTempTableScanRule

type ImplementTempTableScanRule struct {
	// contains filtered or unexported fields
}

ImplementTempTableScanRule converts a TempTableScanExpression to a physical TempTableScanPlan. Mirrors Java's ImplementTempTableScanRule.

func NewImplementTempTableScanRule

func NewImplementTempTableScanRule() *ImplementTempTableScanRule

func (*ImplementTempTableScanRule) Matcher

func (*ImplementTempTableScanRule) OnMatch

type ImplementTypeFilterRule

type ImplementTypeFilterRule struct {
	// contains filtered or unexported fields
}

ImplementTypeFilterRule implements a logical LogicalTypeFilterExpression as a physical RecordQueryTypeFilterPlan, gated on the inner Reference having at least one physical-plan member.

TypeFilter([T1, T2], inner-with-physical-member)
  →  TypeFilterPlan([T1, T2], inner-physical)

Same gating pattern as Implement{Filter,Sort,Distinct}.

Java's ImplementTypeFilterRule consults PlanPartition properties to filter only over partitions producing stored records (not covering-index partitions); Go always emits the simple type-filter.

func NewImplementTypeFilterRule

func NewImplementTypeFilterRule() *ImplementTypeFilterRule

NewImplementTypeFilterRule constructs the rule.

func (*ImplementTypeFilterRule) Matcher

Matcher returns the pattern.

func (*ImplementTypeFilterRule) OnMatch

func (r *ImplementTypeFilterRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires on every LogicalTypeFilterExpression with a physical inner.

type ImplementUniqueRule

type ImplementUniqueRule struct {
	// contains filtered or unexported fields
}

ImplementUniqueRule implements LogicalUniqueExpression one exact physical member at a time. Ordinary Unique is absorbed only when that same member is proven both record-distinct and to carry a primary key. Required Unique is never absorbed: every exact member with a primary key is frozen under a physical PK-distinct plan.

Ports Java's ImplementUniqueRule.

func NewImplementUniqueRule

func NewImplementUniqueRule() *ImplementUniqueRule

func (*ImplementUniqueRule) Matcher

func (*ImplementUniqueRule) OnMatch

func (r *ImplementUniqueRule) OnMatch(call *ImplementationRuleCall)

type ImplementUnorderedUnionRule

type ImplementUnorderedUnionRule struct {
	// contains filtered or unexported fields
}

ImplementUnorderedUnionRule implements LogicalUnionExpression as a RecordQueryUnorderedUnionPlan. It extracts physical plans from each child Reference's plan partitions and creates a concatenating union plan over them.

Ports Java's ImplementUnorderedUnionRule.

func NewImplementUnorderedUnionRule

func NewImplementUnorderedUnionRule() *ImplementUnorderedUnionRule

func (*ImplementUnorderedUnionRule) Matcher

func (*ImplementUnorderedUnionRule) OnMatch

type ImplementUpdateRule

type ImplementUpdateRule struct {
	// contains filtered or unexported fields
}

ImplementUpdateRule implements a logical UpdateExpression as a physical RecordQueryUpdatePlan.

Update(target, [transforms], stored-record-access-path)
  →  UpdatePlan(target, [transforms],
         UnorderedPrimaryKeyDistinct(stored-record-access-path))

Ports Java's ImplementUpdateRule 1:1 on both of its structural decisions:

  • the inner reference is bound only through partitions whose StoredRecordProperty holds (ImplementUpdateRule.java:54-57), and
  • a primary-key dedup is interposed UNCONDITIONALLY between the access path and the mutation (ImplementUpdateRule.java:79-80). The update rule does not consult DistinctRecordsProperty at all — only ImplementDeleteRule does — so neither does this one.

Both are handled by storedRecordDMLCandidates / dmlDedupedInnerQuantifier, where the reasoning for each lives.

Per-row transform application happens at execution time (not rule-fire time) — transforms pass through unchanged.

func NewImplementUpdateRule

func NewImplementUpdateRule() *ImplementUpdateRule

NewImplementUpdateRule constructs the rule.

func (*ImplementUpdateRule) Matcher

Matcher returns the pattern.

func (*ImplementUpdateRule) OnMatch

func (r *ImplementUpdateRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires on every UpdateExpression, once per stored-record access path the inner reference offers.

type ImplementValuesRule

type ImplementValuesRule struct {
	// contains filtered or unexported fields
}

ImplementValuesRule implements a logical LogicalValuesExpression as a physical RecordQueryValuesPlan. Trivial leaf rule — no inner to gate on.

func NewImplementValuesRule

func NewImplementValuesRule() *ImplementValuesRule

func (*ImplementValuesRule) Matcher

func (*ImplementValuesRule) OnMatch

func (r *ImplementValuesRule) OnMatch(call *ExpressionRuleCall)

type ImplementationRule

type ImplementationRule interface {
	Matcher() matching.BindingMatcher
	OnMatch(call *ImplementationRuleCall)
}

ImplementationRule is a rule that runs during PhasePlanning. Like ExpressionRule, it yields expressions into Members via ref.Insert(). It operates on expression partitions and creates physical plan alternatives.

Ports Java's ImplementationCascadesRule.

func AsImplementationRule

func AsImplementationRule(rule ExpressionRule) ImplementationRule

AsImplementationRule adapts an ExpressionRule to run as an ImplementationRule during the PLANNING phase. The wrapped rule's Yield() inserts into Members (via the ImplementationRuleCall) alongside exploration rules. MemoizeExpression uses the planner's Memo when available.

This lets the physical-implementation ExpressionRules (BatchAExpressionRules) run inside the PLANNING phase's ImplementationRule driver without rewriting each rule.

func DefaultImplementationRules

func DefaultImplementationRules() []ImplementationRule

DefaultImplementationRules returns the ImplementationRules for the PLANNING phase.

func GoExtensionImplementationRules

func GoExtensionImplementationRules() []ImplementationRule

GoExtensionImplementationRules returns implementation rules that have no Java equivalent. These extend the Cascades planner with in-memory post-processing operators (RFC-001). Registered separately so the boundary between Java-ported and Go-extension rules is explicit.

type ImplementationRuleCall

type ImplementationRuleCall struct {
	Bindings  *matching.PlannerBindings
	Reference *expressions.Reference
	Context   PlanContext
	// RunContext is nil only for standalone rule tests.
	RunContext  context.Context
	Constraints *ConstraintMap
	Stats       properties.StatisticsProvider
	// contains filtered or unexported fields
}

ImplementationRuleCall provides the restricted API that ImplementationRules are allowed to use. It extends the base RuleCall with Memoizer and Yield operations.

Ports Java's ImplementationCascadesRuleCall.

func (*ImplementationRuleCall) AdoptStagedInserts

func (c *ImplementationRuleCall) AdoptStagedInserts(staged []stagedReExploringInsert)

AdoptStagedInserts takes over another call's held InsertReExploring effects, so a rule running under an adapter commits at the OUTER driver's boundary rather than at the inner one, which cannot see the outer preflight.

func (*ImplementationRuleCall) CancellationErr

func (c *ImplementationRuleCall) CancellationErr() error

CancellationErr reports whether the owning planning run was canceled.

func (*ImplementationRuleCall) CommitStagedInserts

func (c *ImplementationRuleCall) CommitStagedInserts()

CommitStagedInserts applies the held InsertReExploring effects. Drivers call it only after Err is clear and the whole batch has preflighted, and BEFORE publishing the yields, so a parent lands over complete children.

func (*ImplementationRuleCall) CostModel

func (c *ImplementationRuleCall) CostModel() func(a, b expressions.RelationalExpression) bool

CostModel returns the comparator a rule should use for internal best-plan selection: stats-aware when the planner threaded statistics, else the default-stats comparator. Mirrors ExpressionRuleCall.CostModel.

func (*ImplementationRuleCall) Err

func (c *ImplementationRuleCall) Err() error

Err returns the first error reported by the rule body.

func (*ImplementationRuleCall) Fail

func (c *ImplementationRuleCall) Fail(err error)

Fail records the first rule-body failure. Drivers check Err before applying any staged yield or constraint effect.

func (*ImplementationRuleCall) GetRequestedOrderings

func (c *ImplementationRuleCall) GetRequestedOrderings() []*properties.RequestedOrdering

GetRequestedOrderings returns the requested orderings for this Reference, if set by a parent rule. Returns nil if no ordering constraint is set.

func (*ImplementationRuleCall) IsConstraintOnly

func (c *ImplementationRuleCall) IsConstraintOnly() bool

IsConstraintOnly returns true when the rule is firing during the top-down constraint-propagation pass (PLANNING Phase 1). Rules that only push constraints should check this and skip implementation work.

func (*ImplementationRuleCall) MemoizeFinalExpression

func (c *ImplementationRuleCall) MemoizeFinalExpression(
	expr expressions.RelationalExpression,
) *expressions.Reference

MemoizeFinalExpression creates a new Reference holding expr as its single FINAL member — Java's memoizePlan (Reference.ofFinalExpressions). Stage stays CANONICAL: see MemoizeFinalExpressionsFromOther for why the final-set placement and the planner stage are separate decisions.

func (*ImplementationRuleCall) MemoizeFinalExpressionsFromOther

func (c *ImplementationRuleCall) MemoizeFinalExpressionsFromOther(
	source *expressions.Reference,
	exprs []expressions.RelationalExpression,
) *expressions.Reference

MemoizeFinalExpressionsFromOther creates a new Reference containing only the specified expressions (which must already be members of `source`). The new Reference holds them as final members — disentangled from the shared DAG.

Ports Java's FinalMemoizer.memoizeFinalExpressionsFromOther.

func (*ImplementationRuleCall) PushConstraint

func (c *ImplementationRuleCall) PushConstraint(
	childRef *expressions.Reference,
	orderings []*properties.RequestedOrdering,
)

PushConstraint pushes requested orderings to a child Reference, COMBINING with anything already pushed there (Java ConstraintsMap.pushProperty → PlannerConstraint.combine — a subsumption union). A blind replace let the LAST parent pushing onto a shared child clobber every other parent's requirement, and the requested-ordering winner retention in OptimizeGroupTask would then prune the clobbered parents' ordered alternatives.

func (*ImplementationRuleCall) Yield

Yield records a final expression to be inserted into the Reference's final members after the rule completes.

func (*ImplementationRuleCall) YieldFinalExpression

func (c *ImplementationRuleCall) YieldFinalExpression(expr expressions.RelationalExpression)

YieldFinalExpression is an alias for Yield — matches Java's FinalYields.yieldFinalExpression naming.

type InComparisonToExplodeRule

type InComparisonToExplodeRule struct {
	// contains filtered or unexported fields
}

InComparisonToExplodeRule rewrites a LogicalFilterExpression whose predicate list contains a ComparisonPredicate with ComparisonIn.

Single-element IN → simple equality (no union):

Filter([col IN (v1), ...other...], inner)
  →  Filter([col = v1, ...other...], inner)

Multi-element IN → SelectExpression with ExplodeExpression:

Filter([col IN (v1, v2, v3), ...other...], inner)
  →  SelectExpression(
       resultValue = QOV(innerAlias),
       quantifiers = [
         ForEach(Filter([col = QOV(explodeAlias), ...other...], inner)),
         ForEach(Explode([v1, v2, v3])),
       ],
       predicates = [],
     )

Mirrors Java's InComparisonToExplodeRule. The ImplementInJoinRule (PLANNING phase) handles this SelectExpression shape and produces InJoinPlan or InUnionPlan. The inner LogicalFilterExpression's equality predicate (col = QOV(explodeAlias)) is matched by the index-matching infrastructure, which creates an index scan with the column equality-bound to the explode alias. ImplementInJoinRule detects this correlation via the inner plan's RichOrdering.

Guards:

  • At least one ComparisonIn predicate.
  • The IN-list Operand must be PLAN-TIME CONSTANT. Not merely "evaluates without a row context": a list holding a column reference evaluates to a []any with a NULL in it, because fieldValue.Evaluate(nil) answers (nil, nil) rather than an error. See the comment at the check itself for why that distinction is load-bearing.
  • Being constant, it must then evaluate to a non-empty []any.
  • The filter must have an inner Quantifier (no bare filter).

func NewInComparisonToExplodeRule

func NewInComparisonToExplodeRule() *InComparisonToExplodeRule

func (*InComparisonToExplodeRule) Matcher

func (*InComparisonToExplodeRule) OnMatch

type IndexDef

type IndexDef interface {
	IndexName() string
	IndexColumnNames() []string
	IndexRecordTypes() []string
	IndexIsUnique() bool
	IndexPrimaryKeyColumns() []string
}

IndexDef describes a secondary index for PlanContext construction. This is an adapter interface so the cascades package doesn't depend on the recordlayer package directly (avoiding import cycles).

type IndexDefWithColumnFunctions

type IndexDefWithColumnFunctions interface {
	IndexDef
	IndexColumnFunctions() []string
}

IndexDefWithColumnFunctions is an optional extension of IndexDef for indexes whose key columns are not all bare fields. IndexColumnFunctions returns a slice parallel to IndexColumnNames: entry i is the function wrapping the i-th column ("" for a plain field, FunctionKindCardinality for a CARDINALITY() column). A nil/empty return means every column is a plain field. Defs that don't implement this interface are treated as all-plain-field.

type IndexDefWithCommonPrimaryKey

type IndexDefWithCommonPrimaryKey interface {
	IndexDef
	IndexCommonPrimaryKeyValues() []values.Value
}

IndexDefWithCommonPrimaryKey is an optional extension of IndexDef exposing the index's common primary key translated to structure-encoding Values (record-type -key prefixes, nesting) — the input to PrimaryKeyProperty (RFC-189 B3). Defs that don't implement it (or return nil) leave the property abstaining, so the DistinctUnion dedup optimization is disabled rather than risking a wrong one.

type IndexDefWithCreatesDuplicates

type IndexDefWithCreatesDuplicates interface {
	IndexDef
	IndexCreatesDuplicates() bool
}

IndexDefWithCreatesDuplicates is an optional extension of IndexDef for indexes that can state whether their root key expression FANS OUT (a repeated/collection field produces multiple entries per record). Ports Java's index.getRootExpression().createsDuplicates(). A fan-out index does NOT produce distinct records; defs that don't implement this interface remain UNKNOWN, so cardinality-sensitive properties and shortcuts abstain.

type IndexDefWithKeyComponentTypes

type IndexDefWithKeyComponentTypes interface {
	IndexDef
	IndexKeyComponentTypes() []values.Type
}

IndexDefWithKeyComponentTypes is an optional extension for metadata adapters that can derive the authoritative physical tuple type of every index-key component. The slice is parallel to IndexColumnNames.

type IndexDefWithOpaqueFilter

type IndexDefWithOpaqueFilter interface {
	IndexDef
	IndexHasOpaqueFilter() bool
}

IndexDefWithOpaqueFilter reports an index that FILTERS records but whose predicate the planner cannot see — it has no proto representation, so there is nothing to attach to the candidate graph and nothing a matcher can account for.

This exists because sparseness and its SERIALIZED FORM are two different questions, and reading the second as the first fails in the unsafe direction. Go's Index.SetPredicate installs an evaluator CLOSURE and nils the proto; the index maintainers gate entry creation on that closure, so such an index is exactly as sparse on disk as a proto-defined one while presenting `predicateProto == nil` — which every completeness check reads as "full".

It is a Go-only hazard: Java's Index.getPredicate() is parsed from RecordMetaDataProto.Index.predicate and cannot exist without it, so Java has no representation-versus-fact gap to fall through. The obligation is entirely on this side, and the answer is to fail closed — an opaque filter can never be proved tautological, so an index carrying one is never complete and never cardinality-preserving.

type IndexDefWithPredicate

type IndexDefWithPredicate interface {
	IndexDef
	IndexPredicateProto() *gen.Predicate
}

IndexDefWithPredicate is an optional extension of IndexDef for SPARSE (filtered) indexes — indexes whose metadata carries a WHERE predicate (RecordMetaDataProto.Index.predicate). The candidate must know it: a sparse index holds entries only for records matching the predicate, so matching a query against it as if it were a full index returns silently missing rows. Java attaches the predicate to the candidate graph at expansion (ValueIndexExpansionVisitor.java:138-162). A nil return means a full index.

type IndexDefWithPrimaryKeyComponentTypes

type IndexDefWithPrimaryKeyComponentTypes interface {
	IndexDef
	IndexPrimaryKeyComponentTypes() []values.Type
}

IndexDefWithPrimaryKeyComponentTypes optionally supplies authoritative tuple types aligned with IndexPrimaryKeyColumns. These govern the appended PK suffix's ordering congruence; missing metadata conservatively suppresses the suffix rather than inferring a type from its display name.

type IndexDefWithRecordTypeRowTypes

type IndexDefWithRecordTypeRowTypes interface {
	IndexDef
	IndexRecordTypeRowTypes() []values.Type
}

IndexDefWithRecordTypeRowTypes is an optional extension of IndexDef for defs that can state the row layout of EACH record type the index serves.

IndexRowType above answers only for a single-record-type index and degrades to UnknownType otherwise, because a multi-type index's rows have no one layout. That degradation is correct for anything that needs THE layout, and useless for covering-column resolution, which needs a per-type answer: an ordinal is meaningful against one type's descriptor and meaningless against another's (RFC-197 item 1). Defs that don't implement this leave a multi-type candidate with no layout at all, which fails closed — the push is declined rather than resolved against a layout that does not exist.

type IndexDefWithRootKeyExpression

type IndexDefWithRootKeyExpression interface {
	IndexDef
	IndexRootKeyExpression() *gen.KeyExpression
}

IndexDefWithRootKeyExpression is an optional extension of IndexDef for defs that can expose the record-layer key-expression AST. The value-index expansion uses this structure to reproduce Java's fan-out candidate graph (a correlated Explode under an inner Select) instead of flattening a repeated field into an ordinary scalar placeholder. Implementations must return a caller-owned message; NewPlanContextFromIndexDefs defensively clones it again when attaching it to the candidate.

type IndexDefWithRowType

type IndexDefWithRowType interface {
	IndexDef
	IndexRowType() values.Type
}

IndexDefWithRowType is an optional extension of IndexDef for defs that can state the flowed row layout of the records the index serves (the descriptor-shaped positional type). Candidates built without one flow UnknownType, which disqualifies them from plans that must bind comparison keys to row slots at plan time (the pk-merge intersection).

type IndexDefWithValueColumns

type IndexDefWithValueColumns interface {
	IndexDef
	IndexValueColumnNames() []string
}

IndexDefWithValueColumns is an optional extension of IndexDef for covering (KeyWithValue-rooted) indexes. IndexValueColumnNames returns the columns stored in the FDB VALUE — past the split point — which are available for covering translation but are NOT sargable and do NOT order the scan (the physical entry key ends at the split point followed by the primary key). Java models the same split as the expansion's separate valueValues list (ValueIndexExpansionVisitor.java:109-121). Nil/empty means no value part.

type IndexScanPreference

type IndexScanPreference int

IndexScanPreference selects the default direction of the cost model's primary-scan-vs-index-scan tie-break, mirroring Java's RecordQueryPlannerConfiguration.IndexScanPreference proto enum.

const (
	// PreferScan prefers a full/primary scan over an index scan (Cascades
	// default; proto PREFER_SCAN = 0).
	PreferScan IndexScanPreference = iota
	// PreferIndex prefers an index scan (proto PREFER_INDEX = 1).
	PreferIndex
	// PreferPrimaryKeyIndex prefers an index scan on the primary key (proto
	// PREFER_PRIMARY_KEY_INDEX = 2). Java's comparePrimaryScanToIndexScan treats
	// it identically to PREFER_INDEX in the tie-break (prefer the index).
	PreferPrimaryKeyIndex
)

type InitiatePlannerPhaseTask

type InitiatePlannerPhaseTask struct {
	Phase   PlannerPhase
	RootRef *expressions.Reference
}

InitiatePlannerPhaseTask starts a planner phase. Pushed once per phase. LIFO ordering ensures: ExploreGroup fires first, then OptimizeGroup, then the next phase's InitiatePlannerPhaseTask. Mirrors Java's CascadesPlanner.InitiatePlannerPhase.

func (*InitiatePlannerPhaseTask) Run

type IntersectionInfo

type IntersectionInfo struct {
	// contains filtered or unexported fields
}

IntersectionInfo tracks the state of a single data access within the intersection sieve. It carries the access's ordering, compensation, participating expressions, and an estimated max cardinality.

Ports Java's AbstractDataAccessRule.IntersectionInfo.

func IntersectionInfoOfImpossibleAccess

func IntersectionInfoOfImpossibleAccess(
	ordering *properties.RichOrdering,
	comp Compensation,
) *IntersectionInfo

IntersectionInfoOfImpossibleAccess creates an IntersectionInfo for an impossible access (no expressions, unknown cardinality). Mirrors Java's IntersectionInfo.ofImpossibleAccess().

func IntersectionInfoOfIntersection

func IntersectionInfoOfIntersection(
	ordering *properties.RichOrdering,
	comp Compensation,
	exprs []expressions.RelationalExpression,
) *IntersectionInfo

IntersectionInfoOfIntersection creates an IntersectionInfo for a computed intersection (multiple expressions, unknown cardinality). Mirrors Java's IntersectionInfo.ofIntersection().

func IntersectionInfoOfSingleAccess

func IntersectionInfoOfSingleAccess(
	ordering *properties.RichOrdering,
	comp Compensation,
	expr expressions.RelationalExpression,
	maxCard int64,
) *IntersectionInfo

IntersectionInfoOfSingleAccess creates an IntersectionInfo for a single data access (one expression). Mirrors Java's IntersectionInfo.ofSingleAccess().

func NewIntersectionInfo

func NewIntersectionInfo(
	ordering *properties.RichOrdering,
	comp Compensation,
	exprs []expressions.RelationalExpression,
	maxCard int64,
) *IntersectionInfo

NewIntersectionInfo creates an IntersectionInfo with all fields.

func (*IntersectionInfo) EvictExpressions

func (i *IntersectionInfo) EvictExpressions()

EvictExpressions clears the expressions list. Mirrors Java's IntersectionInfo.evictExpressions().

func (*IntersectionInfo) GetCompensation

func (i *IntersectionInfo) GetCompensation() Compensation

GetCompensation returns the compensation for this access.

func (*IntersectionInfo) GetExpressions

func (i *IntersectionInfo) GetExpressions() []expressions.RelationalExpression

GetExpressions returns the participating expressions.

func (*IntersectionInfo) GetMaxCardinality

func (i *IntersectionInfo) GetMaxCardinality() int64

GetMaxCardinality returns the estimated max cardinality. Returns CardinalityUnknown (-1) if unknown.

func (*IntersectionInfo) GetOrdering

func (i *IntersectionInfo) GetOrdering() *properties.RichOrdering

GetOrdering returns the ordering for this access.

type IntersectionMergeRule

type IntersectionMergeRule struct {
	// contains filtered or unexported fields
}

IntersectionMergeRule flattens nested LogicalIntersection expressions, mirroring UnionMergeRule for the intersection operator. The inner intersection's children are promoted into the outer's child list.

Intersection(A, Intersection(B, C), D) [keys=K]
→
Intersection(A, B, C, D) [keys=K]

SQL-equivalent: bag-intersection is associative.

Constraint: the inner Intersection's comparisonKeyValues MUST match the outer's. Different keys means the inner's intersection is computed under a different equality contract; flattening would silently change semantics. Different-keys case declines.

'Match' here is by Explain-text equality of the corresponding keys — the same conservative textual bridge as LogicalProjectionExpression.EqualsWithoutChildren (different-keys cases decline, so semantics are never merged; values.SemanticEquals exists, but switching dedup/match semantics to it needs its own review cycle).

func NewIntersectionMergeRule

func NewIntersectionMergeRule() *IntersectionMergeRule

NewIntersectionMergeRule constructs the rule.

func (*IntersectionMergeRule) Matcher

Matcher returns the pattern.

func (*IntersectionMergeRule) OnMatch

func (r *IntersectionMergeRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when at least one child Quantifier ranges over another Intersection AND every such inner Intersection has matching comparisonKeyValues.

type IntersectionResult

type IntersectionResult struct {
	// contains filtered or unexported fields
}

IntersectionResult captures the result of attempting to intersect multiple data accesses. When viable, it carries a common ordering, a compensation, and the participating expressions.

Ports Java's AbstractDataAccessRule.IntersectionResult.

func NewIntersectionResult

func NewIntersectionResult(
	ordering *properties.RichOrdering,
	comp Compensation,
	exprs []expressions.RelationalExpression,
) *IntersectionResult

NewIntersectionResult creates an IntersectionResult. When commonOrdering is nil the expressions slice must be empty (mirrors Java's Verify.verify precondition).

func NoViableIntersection

func NoViableIntersection() *IntersectionResult

NoViableIntersection returns an IntersectionResult that indicates no viable intersection was found. Mirrors Java's IntersectionResult.noViableIntersection().

func (*IntersectionResult) GetCommonOrdering

func (r *IntersectionResult) GetCommonOrdering() *properties.RichOrdering

GetCommonOrdering returns the common intersection ordering. Panics if the intersection is not viable.

func (*IntersectionResult) GetCompensation

func (r *IntersectionResult) GetCompensation() Compensation

GetCompensation returns the compensation for this intersection.

func (*IntersectionResult) GetExpressions

func (r *IntersectionResult) GetExpressions() []expressions.RelationalExpression

GetExpressions returns the participating expressions.

func (*IntersectionResult) IsViable

func (r *IntersectionResult) IsViable() bool

IsViable reports whether this result represents a viable intersection (i.e. a common ordering was found). Mirrors Java's IntersectionResult.hasViableIntersection().

func (*IntersectionResult) String

func (r *IntersectionResult) String() string

String returns a human-readable representation. Mirrors Java's IntersectionResult.toString().

type IntersectionSingletonElimRule

type IntersectionSingletonElimRule struct {
	// contains filtered or unexported fields
}

IntersectionSingletonElimRule eliminates a LogicalIntersection with exactly one child — INTERSECTION of a single input is just that input.

Intersection([Q]) → inner of Q

func NewIntersectionSingletonElimRule

func NewIntersectionSingletonElimRule() *IntersectionSingletonElimRule

NewIntersectionSingletonElimRule constructs the rule.

func (*IntersectionSingletonElimRule) Matcher

Matcher returns the pattern.

func (*IntersectionSingletonElimRule) OnMatch

OnMatch fires when the intersection has exactly one child.

type IntersectorFunc

type IntersectorFunc func(
	accesses []Vectored[*SingleMatchedAccess],
	requestedOrderings []*properties.RequestedOrdering,
) *IntersectionResult

IntersectorFunc is the function type for computing intersections of multiple data accesses. Concrete rules provide their own intersection logic through this callback, replacing Java's abstract method createIntersectionAndCompensation.

func WithPrimaryKeyIntersector

func WithPrimaryKeyIntersector(ctx PlanContext) IntersectorFunc

WithPrimaryKeyIntersector returns an IntersectorFunc that creates physical intersection plans from compatible partial-match subsets. Every subset must share a structural primary key and a rich directional ordering whose comparison key contains every non-fixed primary-key component.

It creates RecordQueryIntersectionPlan directly (physical, not logical). This avoids the task cascade that would occur if a logical intersection were inserted and then explored — fresh child References trigger re-exploration loops.

type LimitMergeRule

type LimitMergeRule struct {
	// contains filtered or unexported fields
}

LimitMergeRule consolidates nested LIMIT expressions into one.

Pattern:

LogicalLimit(limitOuter, offsetOuter)
  inner → LogicalLimit(limitInner, offsetInner)
    inner → X

Rewrite:

LogicalLimit(effectiveLimit, effectiveOffset)
  inner → X

Semantics: LIMIT a OFFSET b over LIMIT c OFFSET d

The inner produces at most c rows starting at d.
The outer then skips b of those and takes a.
Combined offset = d + b (skip d from source, then b more).
Combined limit = min(a, c - b) — can't take more than inner
  produces minus what outer skips of the inner result.
If c - b <= 0, the combined limit is 0 (no rows).

func NewLimitMergeRule

func NewLimitMergeRule() *LimitMergeRule

func (*LimitMergeRule) Matcher

func (r *LimitMergeRule) Matcher() matching.BindingMatcher

func (*LimitMergeRule) OnMatch

func (r *LimitMergeRule) OnMatch(call *ExpressionRuleCall)

type MappingKey

type MappingKey struct {
	// contains filtered or unexported fields
}

MappingKey captures the relationship between query predicate and candidate predicate. Ports Java's PredicateMultiMap.PredicateMapping.MappingKey.

func NewMappingKey

func NewMappingKey(
	originalQueryPredicate predicates.QueryPredicate,
	candidatePredicate predicates.QueryPredicate,
	mappingKind MappingKind,
) MappingKey

NewMappingKey constructs a MappingKey.

func (MappingKey) GetCandidatePredicate

func (k MappingKey) GetCandidatePredicate() predicates.QueryPredicate

GetCandidatePredicate returns the candidate predicate.

func (MappingKey) GetMappingKind

func (k MappingKey) GetMappingKind() MappingKind

GetMappingKind returns the mapping kind.

func (MappingKey) GetOriginalQueryPredicate

func (k MappingKey) GetOriginalQueryPredicate() predicates.QueryPredicate

GetOriginalQueryPredicate returns the original query predicate.

type MappingKind

type MappingKind int

MappingKind discriminates the kind of predicate mapping. Ports Java's PredicateMultiMap.PredicateMapping.MappingKind.

const (
	// MappingRegularImpliesCandidate means the query predicate
	// directly implies the candidate predicate.
	MappingRegularImpliesCandidate MappingKind = iota

	// MappingOrTermImpliesCandidate means the query predicate is
	// one term of an OR that implies the candidate predicate.
	MappingOrTermImpliesCandidate
)

func (MappingKind) String

func (k MappingKind) String() string

String returns a human-readable name for the MappingKind.

type MatchCandidate

type MatchCandidate interface {
	// CandidateName returns the candidate's identifier (typically the
	// index name, or "primary" for the PK scan).
	CandidateName() string

	// GetTraversal returns the Traversal of this candidate's expression
	// tree, used by matching rules (MatchLeafRule, MatchIntermediateRule)
	// to walk the candidate structure. The traversal must be stable once
	// computed. Returns nil if the candidate has no expression tree
	// (a candidate that does not support traversal-based matching).
	//
	// Ports Java's MatchCandidate.getTraversal().
	GetTraversal() *Traversal

	// GetColumnNames returns the ordered column-name list (one per
	// index key column, parallel to GetSargableAliases). Used by rules
	// to match ComparisonPredicate field references against the index's
	// key columns.
	GetColumnNames() []string

	// GetSargableAliases returns the ordered list of parameter
	// identifiers (one per index key column, left-to-right) that can
	// be bound by predicate matching. The order determines the index
	// key prefix discipline: N equality-bound parameters followed by
	// at most one inequality-bound parameter form a valid scan prefix.
	GetSargableAliases() []values.CorrelationIdentifier

	// GetRecordTypes returns which record types this candidate covers.
	GetRecordTypes() []string

	// IsUnique reports whether the candidate's key uniquely identifies
	// a record (unique index or primary key).
	IsUnique() bool

	// ComputeBoundParameterPrefixMap computes the subset of parameter bindings
	// that this candidate's physical scan can consume. For ordinary ordered
	// scans that is the longest valid sargable prefix (N equalities plus an
	// optional trailing inequality); specialized candidates may retain
	// independent index-only bindings as well.
	ComputeBoundParameterPrefixMap(
		bindings map[values.CorrelationIdentifier]*predicates.ComparisonRange,
	) map[values.CorrelationIdentifier]*predicates.ComparisonRange

	// ToScanPlan converts a successful match into a physical index
	// scan plan. Called with the prefix map from
	// ComputeBoundParameterPrefixMap + reverse flag.
	ToScanPlan(
		prefixMap map[values.CorrelationIdentifier]*predicates.ComparisonRange,
		reverse bool,
	) plans.RecordQueryPlan
}

MatchCandidate represents a scan candidate — typically a secondary index or the primary-key scan. During the EXPLORE phase, the planner matches query predicates against each candidate's sargable parameters; during OPTIMIZE it converts successful matches into physical index-scan plans.

Ports the core surface of Java's `com.apple.foundationdb.record.query.plan.cascades.MatchCandidate`.

func GetPartialMatchCandidatesTyped

func GetPartialMatchCandidatesTyped(
	ref *expressions.Reference,
) []MatchCandidate

GetPartialMatchCandidatesTyped returns all MatchCandidates that have partial matches on the Reference. Typed wrapper around Reference.GetPartialMatchCandidates.

type MatchInfo

type MatchInfo interface {
	// GetMatchedOrderingParts returns the ordering parts matched by
	// this match info.
	GetMatchedOrderingParts() []*MatchedOrderingPart

	// GetMaxMatchMap returns the maximum match map between query and
	// candidate value subtrees.
	GetMaxMatchMap() *MaxMatchMap

	// IsAdjusted reports whether this MatchInfo was produced by
	// adjusting (wrapping) another MatchInfo.
	IsAdjusted() bool

	// IsRegular reports whether this MatchInfo is a direct
	// (non-adjusted) match.
	IsRegular() bool

	// GetRegularMatchInfo returns the underlying RegularMatchInfo.
	// For a RegularMatchInfo, it returns itself. For an
	// AdjustedMatchInfo, it delegates to the underlying MatchInfo.
	GetRegularMatchInfo() *RegularMatchInfo

	// GetGroupByMappings returns the group-by mappings captured
	// during matching.
	GetGroupByMappings() *GroupByMappings
}

MatchInfo represents the result of matching one expression against an expression from a MatchCandidate.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.MatchInfo.

type MatchIntermediateRule

type MatchIntermediateRule struct {
	// contains filtered or unexported fields
}

MatchIntermediateRule is the Cascades rule that matches non-leaf query expressions (those with quantifiers) against candidate expressions by composing child PartialMatches. For every query expression with at least one quantifier, the rule:

  1. Collects the child References from the expression's quantifiers.
  2. Finds which MatchCandidates have PartialMatches on those child References (seeded by MatchLeafRule or earlier MatchIntermediateRule firings).
  3. For each such candidate, walks upward through the candidate's Traversal to find parent expressions that reference the candidate-side References from those PartialMatches.
  4. Attempts a structural match between the query expression and each candidate parent expression, verifying that every quantifier pair is backed by a child PartialMatch.
  5. On match, creates a new composite PartialMatch and stores it on the query Reference.

This rule propagates matches upward from leaves, enabling multi-level expression trees to be matched against candidate (index) expression trees. It prepares AdjustMatchRule and physical-implementation rules to produce index-scan plans.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.rules.MatchIntermediateRule. Like Java's RelationalExpression.match(), the structural path enumerates dependency-sound quantifier bijections and every compatible child-match branch. A strict shared visit budget may truncate the deterministic search as a safe optimization miss; it never falls back to positional pairing or relaxes a semantic gate.

func NewMatchIntermediateRule

func NewMatchIntermediateRule() *MatchIntermediateRule

NewMatchIntermediateRule constructs a MatchIntermediateRule.

func (*MatchIntermediateRule) Matcher

Matcher returns the binding matcher. Matches any RelationalExpression (the non-leaf check is inside OnMatch). Mirrors Java's MatchIntermediateRule which returns Optional.empty() from getRootOperator().

func (*MatchIntermediateRule) OnMatch

func (r *MatchIntermediateRule) OnMatch(call *ExpressionRuleCall)

OnMatch implements the intermediate matching logic. It collects child References, finds candidates with child PartialMatches, walks upward through each candidate's Traversal, and attempts structural matching at each candidate parent expression.

type MatchLeafRule

type MatchLeafRule struct {
	// contains filtered or unexported fields
}

MatchLeafRule is the Cascades rule that seeds the partial-match infrastructure by matching query leaf expressions against candidate leaf expressions. For every query expression with zero quantifiers, the rule iterates all MatchCandidates from the PlanContext, walks each candidate's Traversal to find its leaf References, and attempts a structural match (EqualsWithoutChildren) between the query leaf and each candidate leaf. On match, a PartialMatch is created and stored on the query Reference.

This rule seeds the memoisation structure for partial matches kept on Reference. It prepares further rules such as MatchIntermediateRule and AdjustMatchRule.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.rules.MatchLeafRule.

func NewMatchLeafRule

func NewMatchLeafRule() *MatchLeafRule

NewMatchLeafRule constructs a MatchLeafRule.

func (*MatchLeafRule) Matcher

func (r *MatchLeafRule) Matcher() matching.BindingMatcher

Matcher returns the binding matcher. Matches any RelationalExpression (the leaf check is performed inside OnMatch). This mirrors Java's MatchLeafRule which returns Optional.empty() from getRootOperator() so it fires on all expression types.

func (*MatchLeafRule) OnMatch

func (r *MatchLeafRule) OnMatch(call *ExpressionRuleCall)

OnMatch iterates all MatchCandidates, finds leaf references in each candidate's Traversal, and attempts a structural match between the query expression and each candidate leaf expression.

type MatchPartition

type MatchPartition struct {
	// contains filtered or unexported fields
}

MatchPartition groups PartialMatch instances for a specific expression and reference. Simple container.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.MatchPartition.

func NewMatchPartition

func NewMatchPartition(matches []PartialMatch) *MatchPartition

NewMatchPartition constructs a MatchPartition from the given matches. The slice is defensively copied.

func (*MatchPartition) GetPartialMatches

func (p *MatchPartition) GetPartialMatches() []PartialMatch

GetPartialMatches returns the partial matches in this partition.

type MatchedOrderingPart

type MatchedOrderingPart struct {
	// contains filtered or unexported fields
}

MatchedOrderingPart is an OrderingPart that has been bound by a comparison during graph matching. It stores the parameter identifier, the value being ordered, the comparison range that was matched (equality/inequality/empty), and the matched sort direction.

Mirrors Java's OrderingPart.MatchedOrderingPart.

func NewMatchedOrderingPart

func NewMatchedOrderingPart(
	parameterId values.CorrelationIdentifier,
	value values.Value,
	comparisonRange *predicates.ComparisonRange,
	matchedSortOrder MatchedSortOrder,
) *MatchedOrderingPart

NewMatchedOrderingPart creates a MatchedOrderingPart. If comparisonRange is nil, it defaults to the empty (universe) range. Mirrors Java's MatchedOrderingPart.of() factory.

func (*MatchedOrderingPart) GetComparisonRange

func (m *MatchedOrderingPart) GetComparisonRange() *predicates.ComparisonRange

GetComparisonRange returns the comparison range.

func (*MatchedOrderingPart) GetComparisonRangeType

func (m *MatchedOrderingPart) GetComparisonRangeType() predicates.ComparisonRangeType

GetComparisonRangeType delegates to the comparison range's GetRangeType.

func (*MatchedOrderingPart) GetMatchedSortOrder

func (m *MatchedOrderingPart) GetMatchedSortOrder() MatchedSortOrder

GetMatchedSortOrder returns the matched sort direction.

func (*MatchedOrderingPart) GetParameterId

func (m *MatchedOrderingPart) GetParameterId() values.CorrelationIdentifier

GetParameterId returns the correlation identifier for this ordering part in the match candidate.

func (*MatchedOrderingPart) GetValue

func (m *MatchedOrderingPart) GetValue() values.Value

GetValue returns the value being ordered by.

func (*MatchedOrderingPart) String

func (m *MatchedOrderingPart) String() string

String returns a human-readable representation.

type MatchedSortOrder

type MatchedSortOrder int

MatchedSortOrder represents the sort direction assigned during index matching. Mirrors Java's OrderingPart.MatchedSortOrder. All values are directional (IsDirectional always returns true).

The naming "ascending" / "descending" is conventional: ascending becomes actual ascending with a forward scan; with a reverse scan it flips. The only semantic invariant is that ascending and descending are polar opposites.

const (
	MatchedSortOrderAscending MatchedSortOrder = iota
	MatchedSortOrderDescending
	MatchedSortOrderAscendingNullsLast
	MatchedSortOrderDescendingNullsFirst
)

func (MatchedSortOrder) ArrowIndicator

func (s MatchedSortOrder) ArrowIndicator() string

ArrowIndicator returns a directional arrow for pretty-printing, matching Java's getArrowIndicator(). Uses the same Unicode arrows as ProvidedSortOrder for the corresponding direction.

func (MatchedSortOrder) IsAnyAscending

func (s MatchedSortOrder) IsAnyAscending() bool

IsAnyAscending reports whether this sort order is any ascending variant.

func (MatchedSortOrder) IsAnyDescending

func (s MatchedSortOrder) IsAnyDescending() bool

IsAnyDescending reports whether this sort order is any descending variant.

func (MatchedSortOrder) IsCounterflowNulls

func (s MatchedSortOrder) IsCounterflowNulls() bool

IsCounterflowNulls reports whether the NULL placement runs against the natural tuple order for this direction (ASC_NULLS_LAST / DESC_NULLS_FIRST). Mirrors Java TupleOrdering.Direction.isCounterflowNulls() via MatchedSortOrder's Direction.

func (MatchedSortOrder) IsDirectional

func (s MatchedSortOrder) IsDirectional() bool

IsDirectional returns true. All MatchedSortOrder values are directional by definition (unlike ProvidedSortOrder which has FIXED and CHOOSE).

func (MatchedSortOrder) String

func (s MatchedSortOrder) String() string

String returns a human-readable label for the sort order.

func (MatchedSortOrder) ToProvidedSortOrder

func (s MatchedSortOrder) ToProvidedSortOrder(isReverse bool) properties.ProvidedSortOrder

ToProvidedSortOrder maps this matched sort order to the corresponding ProvidedSortOrder. When isReverse is true, the direction is flipped (ascending becomes descending, etc.).

The mapping is by direction (Java's Direction enum), not by Go constant name. Both enums use the same iota ordering that maps to the same underlying Direction values.

type MaxMatchMap

type MaxMatchMap struct {
	// contains filtered or unexported fields
}

MaxMatchMap represents the maximum matching between query and candidate Value subtrees. Each entry maps a query sub-value to a candidate sub-value (keyed by ExplainValue for structural equality, matching Java's BiMap approach).

Ports Java's com.apple.foundationdb.record.query.plan.cascades.values.translation.MaxMatchMap.

func ComputeMaxMatchMap

func ComputeMaxMatchMap(
	queryValue values.Value,
	candidateValue values.Value,
	rangedOverAliases map[values.CorrelationIdentifier]struct{},
) *MaxMatchMap

ComputeMaxMatchMap creates a MaxMatchMap by finding the maximum matching between query and candidate value trees.

Ports Java's MaxMatchMap.compute.

func ComputeMaxMatchMapWithEquivalence

func ComputeMaxMatchMapWithEquivalence(
	queryValue values.Value,
	candidateValue values.Value,
	rangedOverAliases map[values.CorrelationIdentifier]struct{},
	valueEquivalence ValueEquivalence,
) *MaxMatchMap

ComputeMaxMatchMapWithEquivalence is like ComputeMaxMatchMap but accepts an optional ValueEquivalence for cross-alias matching. When structural equality fails, the ValueEquivalence is consulted to determine whether two values should be considered equal.

Ports Java's MaxMatchMap.compute(queryValue, candidateValue, rangedOverAliases, valueEquivalence).

func NewMaxMatchMap

func NewMaxMatchMap(
	mapping map[values.Value]values.Value,
	queryValue values.Value,
	candidateValue values.Value,
) *MaxMatchMap

NewMaxMatchMap constructs a MaxMatchMap from a pre-built mapping. The mapping is defensively copied. Retained for backwards compatibility with existing call sites.

func (*MaxMatchMap) AdjustMaybe

func (m *MaxMatchMap) AdjustMaybe(
	upperCandidateAlias values.CorrelationIdentifier,
	upperCandidateResultValue values.Value,
	rangedOverAliases map[values.CorrelationIdentifier]struct{},
) (*MaxMatchMap, bool)

AdjustMaybe adjusts this MaxMatchMap through an upper candidate level. It translates the query value through upperCandidateAlias, then re-computes a MaxMatchMap against upperCandidateResultValue.

Returns (adjustedMap, true) on success, (nil, false) on failure.

Ports Java's MaxMatchMap.adjustMaybe.

func (*MaxMatchMap) GetCandidateValue

func (m *MaxMatchMap) GetCandidateValue() values.Value

GetCandidateValue returns the root candidate value.

func (*MaxMatchMap) GetMap

func (m *MaxMatchMap) GetMap() map[values.Value]values.Value

GetMap returns the value-to-value mapping in the legacy format (map[values.Value]values.Value). This is the query→candidate direction.

func (*MaxMatchMap) GetQueryValue

func (m *MaxMatchMap) GetQueryValue() values.Value

GetQueryValue returns the root query value.

func (*MaxMatchMap) PullUpMaybe

func (m *MaxMatchMap) PullUpMaybe(
	queryAlias values.CorrelationIdentifier,
	candidateAlias values.CorrelationIdentifier,
) (*RegularTranslationMap, bool)

PullUpMaybe creates a TranslationMap that translates queryAlias references to the query value expressed through candidateAlias.

Returns (translationMap, true) on success, (nil, false) on failure.

Ports Java's MaxMatchMap.pullUpMaybe.

func (*MaxMatchMap) Size

func (m *MaxMatchMap) Size() int

Size returns the number of entries in the mapping.

func (*MaxMatchMap) TranslateQueryValueMaybe

func (m *MaxMatchMap) TranslateQueryValueMaybe(
	candidateAlias values.CorrelationIdentifier,
) values.Value

TranslateQueryValueMaybe translates the query value so that it can be expressed in terms of candidateAlias.

For the identity case (query == candidate structurally, single mapping entry), returns a QuantifiedObjectValue(candidateAlias).

For non-identity mappings, uses values.Replace to substitute each mapped query subtree with a pulled-up reference through candidateAlias, then validates that no rangedOverAliases remain in the result.

Returns nil if the translation fails.

Ports Java's MaxMatchMap.translateQueryValueMaybe.

type Memo

type Memo struct {
	// contains filtered or unexported fields
}

Memo is the central memoization structure for the Cascades planner. It tracks all References in the plan DAG and enables cross-Reference equivalence-class sharing: when two rules independently derive structurally-equivalent sub-expressions, the Memo routes them into the same Reference so the planner explores/optimizes that sub-tree only once.

Ports the memoization behaviour from Java's CascadesRuleCall + Traversal. Java uses a MutableNetwork<Reference, ReferencePath> to track the DAG topology; Go uses flat index maps for the same purpose.

Lifecycle:

  • Created via NewMemo(rootRef) at planner construction time.
  • Rules call MemoizeExpression(expr) to find-or-create a Reference for a sub-expression. Returns an existing Reference when the expr (or a structural equivalent) is already memoized.
  • The Planner's EXPLORE phase populates the Memo as rules fire.

Thread safety: single-threaded (same as Java's planner).

func NewMemo

func NewMemo(root *expressions.Reference) *Memo

NewMemo constructs a Memo rooted at `root` and indexes the full DAG reachable from it. If root is nil, returns an empty Memo.

func (*Memo) AddExpression

func (m *Memo) AddExpression(ref *expressions.Reference, expr expressions.RelationalExpression)

AddExpression registers a new expression into the Memo's index within an existing Reference. Call this after Reference.Insert succeeds to keep the Memo's topology index up to date.

func (*Memo) AdmissionErr

func (m *Memo) AdmissionErr() error

AdmissionErr returns the first admission failure, if any. The planner's run loop drains it into capErr so a violated invariant ends the run rather than producing a plan over a memo that never agreed on its own types.

func (*Memo) AliasAwareDedups

func (m *Memo) AliasAwareDedups() int

AliasAwareDedups sums, over every Reference in the memo, the extra dedup the alias-aware interning tier performed (Reference.AliasAwareDedups): proposals for which only tier 3's MemoEqual recognized the duplicate. Prepared ExpressionRule admission now absorbs earlier memo-equal proposals before scheduling their phantom descendants, so the chain corpus legitimately has a zero shadow; the prepared-equality unit test separately pins a direct alias-aware-only hit.

func (*Memo) ContainsReference

func (m *Memo) ContainsReference(ref *expressions.Reference) bool

ContainsReference reports whether the Memo has indexed `ref`.

func (*Memo) InsertReExploring

func (m *Memo) InsertReExploring(ref *expressions.Reference, expr expressions.RelationalExpression) bool

InsertReExploring inserts expr into ref, keeps the topology index fresh, and — when ref's exploration already began — re-arms and schedules the re-round the epoch model requires for out-of-band member growth. Rule code inserting into a ref it did NOT just create must use this, never a raw Reference.Insert.

func (*Memo) Integrate

func (m *Memo) Integrate(ref *expressions.Reference, expr expressions.RelationalExpression)

Integrate is the cross-group merge entry point, called when a rule yields an expression into ref during REWRITING. It either:

  • discovers that a structurally-equivalent member already lives in a DIFFERENT Reference and merges the two groups, or
  • records ref as a parent of expr's child References in the topology index (so future lookups can find this parent).

After the initial step it drains the recursive bottom-up worklist: a merge can make the merged group's parents duplicates of one another, which must themselves merge (the paper's recursive integration).

func (*Memo) MarkPlanningActive

func (m *Memo) MarkPlanningActive()

MarkPlanningActive records that the PLANNING phase has begun; any cross-group merge after this point panics (RFC-037 §0).

func (*Memo) MemoizeExpression

func (m *Memo) MemoizeExpression(expr expressions.RelationalExpression) *expressions.Reference

MemoizeExpression is the core memoization entry point. Given an expression, it either:

  • finds an existing Reference in the Memo that already contains a structurally-equivalent expression (same node info under EqualsWithoutChildren + same child References by pointer), and returns that Reference; or
  • creates a new single-member Reference for the expression, registers it in the Memo, and returns the new Reference.

This is how cross-Reference sharing works: two rules that independently produce the same sub-expression get back the SAME Reference, avoiding redundant exploration.

Mirrors Java's CascadesRuleCall.memoizeExploratoryExpressions.

func (*Memo) MemoizeExpressions

func (m *Memo) MemoizeExpressions(exprs []expressions.RelationalExpression) *expressions.Reference

MemoizeExpressions memoizes multiple expressions into a single Reference. If an existing Reference contains ALL of the given expressions (or structural equivalents), returns that Reference. Otherwise creates a new Reference holding all expressions.

Used when a rule produces multiple equivalent alternatives for the same sub-tree.

func (*Memo) MergeCount

func (m *Memo) MergeCount() int

MergeCount returns the number of cross-group merges performed so far (RFC-037). Used by tests to assert the merge optimization fires.

func (*Memo) NextMergeAlias

func (m *Memo) NextMergeAlias() values.CorrelationIdentifier

NextMergeAlias returns a per-plan deterministic, collision-PROOF quantifier alias for a PartitionSelectRule merge sub-join (RFC-077 7.5).

The alias embeds a double-quote ("). That is the one character no parsed SQL identifier can ever contain: the lexer's delimited-identifier rule is DOUBLE_QUOTE_ID: '"' ~'"'+ '"' (RelationalLexer.g4) — a quoted identifier is any run of NON-quote characters between quotes, so the quotes are stripped and the resulting name can never include a ". A bare "$m"-prefix is NOT safe on its own: a user could write a quoted alias `AS "$m1"`, which parses to the name "$m1" and would collide with this merge quantifier, corrupting alias-keyed binding/rebasing in a multi-way join. (This collision class also affects UniqueCorrelationIdentifier's "q$N" — `AS "q$1"` — a pre-existing, separate hardening item; here we make the merge alias uncollidable outright.)

The per-Memo ordinal makes the alias deterministic across plannings of the same query (for a stable plan hash) while still differing per merge occurrence, so equivalent sub-products intern via the alias-aware Reference.Insert tier, not via a stable string. The alias is internal — never re-lexed as SQL; it only appears as a correlation key (rebasing, NLJ source alias, Explain). See mergeAliasCounter.

func (*Memo) References

func (m *Memo) References() map[*expressions.Reference]struct{}

References returns all References known to the Memo. The returned map is read-only; callers must not mutate it.

func (*Memo) RegisterReference

func (m *Memo) RegisterReference(ref *expressions.Reference)

RegisterReference adds a Reference (and its sub-tree) to the Memo's index without performing memoization lookup. Used when a rule creates a Reference that is known-fresh (e.g. the root at construction time, or a Reference already checked by MemoizeExpression).

func (*Memo) Root

func (m *Memo) Root() *expressions.Reference

Root returns the root Reference of the Memo.

func (*Memo) ScheduleFreshReference

func (m *Memo) ScheduleFreshReference(ref *expressions.Reference)

ScheduleFreshReference registers a rule-minted reference AND schedules its exploration directly. A fresh reference normally explores when a parent's child-walk reaches it — but a reference minted by MemoizeExpression's self-collision arm can be wired into a yielded tree whose own insertion deduped (no parent walk ever comes), leaving the group permanently unscheduled and its logical member unimplemented (the no-plan strand). Java's addNewReference path never produces unscheduled references; this is its Go equivalent for the out-of-band mint.

func (*Memo) SetReExploreScheduler

func (m *Memo) SetReExploreScheduler(f func(*expressions.Reference))

SetReExploreScheduler registers the planner's re-exploration hook (see the reExplore field).

func (*Memo) TotalMembers

func (m *Memo) TotalMembers() int

TotalMembers sums the exploratory + final member count over every canonical Reference in the memo. The shadow-delta pin compares this population with the alias-aware tier disabled. A non-zero difference means tier 3 changed the realized population; zero means an earlier equality tier or prepared admission already prevented the proposal from producing descendants.

type MergeFetchIntoCoveringIndexRule

type MergeFetchIntoCoveringIndexRule struct {
	// contains filtered or unexported fields
}

MergeFetchIntoCoveringIndexRule eliminates a FetchFromPartialRecordPlan when its inner is a covering index scan. If the index provides all needed columns (the fetch is redundant), the rule yields just the inner index scan plan directly.

Pattern:

Fetch(CoveringIndexScan)  →  IndexScan

Mirrors Java's `MergeFetchIntoCoveringIndexRule` one for one, including what it yields: the inner INDEX plan, which fetches its own records by primary key. The rule fires when the fetch wraps a covering scan directly (no intervening filter or distinct).

It was documented as unreachable, because coveringness was a flag that the access path applied only by STRIPPING the fetch. Now the access path emits Fetch(Covering(IndexScan)) on every value-index match (RFC-220), which is exactly this rule's pattern, so it is live.

func NewMergeFetchIntoCoveringIndexRule

func NewMergeFetchIntoCoveringIndexRule() *MergeFetchIntoCoveringIndexRule

func (*MergeFetchIntoCoveringIndexRule) Matcher

func (*MergeFetchIntoCoveringIndexRule) OnMatch

type MergeProjectionAndFetchRule

type MergeProjectionAndFetchRule struct {
	// contains filtered or unexported fields
}

MergeProjectionAndFetchRule removes both a LogicalProjectionExpression and a FetchFromPartialRecordPlan when all projected values are available in the partial record (index entry) before the fetch.

If every projected value can be pushed through the fetch (translated from the full-record domain to the partial-record domain), then neither the projection nor the fetch is needed: the fetch's inner (covering index scan) already provides all necessary data.

Before:

Projection(Fetch(inner))

After (when all values pushable):

inner

Mirrors Java's MergeProjectionAndFetchRule.

func NewMergeProjectionAndFetchRule

func NewMergeProjectionAndFetchRule() *MergeProjectionAndFetchRule

func (*MergeProjectionAndFetchRule) Matcher

func (*MergeProjectionAndFetchRule) OnMatch

type MergeSlotTypingCounts

type MergeSlotTypingCounts struct {
	Typed     int
	Scavenged int
	Scalar    int
	Untyped   int
	Slots     int
}

MergeSlotTypingCounts is the census tally. The four classes must partition Slots; that identity is what makes a share of them meaningful.

func MergeSlotTypingCensus

func MergeSlotTypingCensus() MergeSlotTypingCounts

MergeSlotTypingCensus returns the current tally.

type NoOpFilterRule

type NoOpFilterRule struct {
	// contains filtered or unexported fields
}

NoOpFilterRule eliminates a LogicalFilter whose predicate list is empty OR whose predicates all evaluate (under the constant fold) to a tri-state TRUE. The replacement is the inner Quantifier's expression — the filter is a row-by-row identity at that point.

Two firing conditions, both equivalent SQL no-ops:

  1. Empty predicate list — comes up after FilterMergeRule + a follow-on rule that folds out tautologies, leaving an empty conjunction.
  2. All predicates are ConstantPredicate(TriTrue) — the trivial `WHERE TRUE` shape.

Yields the inner expression directly, NOT a fresh wrapper. The memo dedup absorbs duplicate inserts via Reference.Insert.

func NewNoOpFilterRule

func NewNoOpFilterRule() *NoOpFilterRule

NewNoOpFilterRule constructs the rule.

func (*NoOpFilterRule) Matcher

func (r *NoOpFilterRule) Matcher() matching.BindingMatcher

Matcher returns the pattern.

func (*NoOpFilterRule) OnMatch

func (r *NoOpFilterRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the filter is a no-op.

type NoOpLimitElimRule

type NoOpLimitElimRule struct {
	// contains filtered or unexported fields
}

NoOpLimitElimRule eliminates a LIMIT that has no effect. A LIMIT with limit < 0 (no cap) AND offset == 0 (no skip) is a pure pass-through — the inner expression is equivalent.

Pattern:

LogicalLimit(limit<0, offset=0)
  inner → X

Rewrite: X

func NewNoOpLimitElimRule

func NewNoOpLimitElimRule() *NoOpLimitElimRule

func (*NoOpLimitElimRule) Matcher

func (*NoOpLimitElimRule) OnMatch

func (r *NoOpLimitElimRule) OnMatch(call *ExpressionRuleCall)

type NormalizePredicatesRule

type NormalizePredicatesRule struct {
	// contains filtered or unexported fields
}

NormalizePredicatesRule converts the predicates of a SelectExpression into conjunctive normal form (CNF) — AND of ORs. The normalised predicates are set as the new predicate list on a freshly yielded SelectExpression (quantifiers are rebuilt with new aliases pointing at the same References).

Ports Java's NormalizePredicatesRule which is the precursor to PredicateToLogicalUnionRule. The CNF form makes each OR clause independently matchable by index-pushdown rules, enabling OR-to- UNION transformations.

Algorithm:

  1. AND all predicates together.
  2. Run CNF normalization (distribute OR over AND).
  3. If the result is already in CNF, bail (nothing to do).
  4. Extract the top-level AND conjuncts as the new predicate list.
  5. Yield a new SelectExpression with rebuilt quantifiers.

The normalizer respects a complexity threshold (cnfSizeLimit) to avoid exponential blow-up from deeply nested OR/AND trees. If the normalised form would exceed the limit, the rule produces no yield.

Mirrors Java's BooleanPredicateNormalizer in CNF mode with a default size limit of 1,000,000.

func NewNormalizePredicatesRule

func NewNormalizePredicatesRule() *NormalizePredicatesRule

func (*NormalizePredicatesRule) Matcher

func (*NormalizePredicatesRule) OnMatch

func (r *NormalizePredicatesRule) OnMatch(call *ExpressionRuleCall)

type NotComparisonRewriteRule

type NotComparisonRewriteRule struct {
	// contains filtered or unexported fields
}

NotComparisonRewriteRule pushes a NOT past a ComparisonPredicate whose comparison type has a direct negation: `NOT(x = 5)` → `x <> 5`, `NOT(x IS NULL)` → `x IS NOT NULL`. Leaves `NOT(x IN (...))` and `NOT(x STARTS_WITH 'pre')` alone — those have no direct-negation comparison type.

Mirrors Java's predicate-simplification passes that push NOT down to leaves so downstream index-pushdown rules see a canonical leaf-level predicate and don't have to also handle NOT wrappers.

func NewNotComparisonRewriteRule

func NewNotComparisonRewriteRule() *NotComparisonRewriteRule

NewNotComparisonRewriteRule constructs the rule.

func (*NotComparisonRewriteRule) Matcher

func (*NotComparisonRewriteRule) OnMatch

func (r *NotComparisonRewriteRule) OnMatch(call *RuleCall)

type NotConstantSimplifyRule

type NotConstantSimplifyRule struct {
	// contains filtered or unexported fields
}

NotConstantSimplifyRule folds NOT over a constant child per Kleene NOT (NOT TRUE=FALSE, NOT FALSE=TRUE, NOT UNKNOWN=UNKNOWN). Also fires on NOT NOT x → x (double-negation elimination).

func NewNotConstantSimplifyRule

func NewNotConstantSimplifyRule() *NotConstantSimplifyRule

NewNotConstantSimplifyRule constructs the rule.

func (*NotConstantSimplifyRule) Matcher

func (*NotConstantSimplifyRule) OnMatch

func (r *NotConstantSimplifyRule) OnMatch(call *RuleCall)

type OptimizeGroupTask

type OptimizeGroupTask struct {
	Phase PlannerPhase
	Ref   *expressions.Reference
	// contains filtered or unexported fields
}

OptimizeGroupTask picks the best final expression and prunes losers. Mirrors Java's CascadesPlanner.OptimizeGroup.

func (*OptimizeGroupTask) Run

func (t *OptimizeGroupTask) Run(ctx context.Context, p *Planner)

type OptimizeInputsTask

type OptimizeInputsTask struct {
	Phase PlannerPhase
	Ref   *expressions.Reference
	Expr  expressions.RelationalExpression
}

OptimizeInputsTask pushes OptimizeGroup for each child quantifier. Mirrors Java's CascadesPlanner.OptimizeInputs.

func (*OptimizeInputsTask) Run

func (t *OptimizeInputsTask) Run(ctx context.Context, p *Planner)

type OrAbsorbAndRule

type OrAbsorbAndRule struct {
	// contains filtered or unexported fields
}

OrAbsorbAndRule: mirror. Inside an OR, any AND child that contains a sibling is redundant — drop it. `OR(p, AND(p, q))` → `OR(p)` → `p`.

func NewOrAbsorbAndRule

func NewOrAbsorbAndRule() *OrAbsorbAndRule

NewOrAbsorbAndRule constructs the rule.

func (*OrAbsorbAndRule) Matcher

func (*OrAbsorbAndRule) OnMatch

func (r *OrAbsorbAndRule) OnMatch(call *RuleCall)

type OrConstantSimplifyRule

type OrConstantSimplifyRule struct {
	// contains filtered or unexported fields
}

OrConstantSimplifyRule matches an OrPredicate and folds constant children per Kleene OR identities.

func NewOrConstantSimplifyRule

func NewOrConstantSimplifyRule() *OrConstantSimplifyRule

NewOrConstantSimplifyRule constructs the rule.

func (*OrConstantSimplifyRule) Matcher

func (*OrConstantSimplifyRule) OnMatch

func (r *OrConstantSimplifyRule) OnMatch(call *RuleCall)

type OrDedupRule

type OrDedupRule struct {
	// contains filtered or unexported fields
}

OrDedupRule: mirror of AndDedupRule.

func NewOrDedupRule

func NewOrDedupRule() *OrDedupRule

NewOrDedupRule constructs the rule.

func (*OrDedupRule) Matcher

func (r *OrDedupRule) Matcher() matching.BindingMatcher

func (*OrDedupRule) OnMatch

func (r *OrDedupRule) OnMatch(call *RuleCall)

type OrFlattenRule

type OrFlattenRule struct {
	// contains filtered or unexported fields
}

OrFlattenRule: mirror of AndFlattenRule for OR.

func NewOrFlattenRule

func NewOrFlattenRule() *OrFlattenRule

NewOrFlattenRule constructs the rule.

func (*OrFlattenRule) Matcher

func (r *OrFlattenRule) Matcher() matching.BindingMatcher

func (*OrFlattenRule) OnMatch

func (r *OrFlattenRule) OnMatch(call *RuleCall)

type OrderedIndexScanRule

type OrderedIndexScanRule struct {
	// contains filtered or unexported fields
}

OrderedIndexScanRule matches a LogicalSort over a FullUnorderedScan (no filter in between) and produces an index scan when an index's column order provides the requested sort ordering. The index scan has no predicate bounds — it scans the full index but in the index's key order, eliminating the sort.

Sort([col1 ASC, col2 ASC]) over FullUnorderedScan
  → IndexScan(full-range, index on (col1, col2, ...))

This complements ImplementIndexScanRule (which requires a Filter). When both a predicate and ordering are requested, PushFilterThroughSort moves the filter below the sort, and ImplementIndexScanRule handles the Filter(Scan) shape. This rule covers the pure ORDER BY case.

func NewOrderedIndexScanRule

func NewOrderedIndexScanRule() *OrderedIndexScanRule

func (*OrderedIndexScanRule) Matcher

func (*OrderedIndexScanRule) OnMatch

func (r *OrderedIndexScanRule) OnMatch(call *ExpressionRuleCall)

type OrderedPrimaryScanRule

type OrderedPrimaryScanRule struct {
	// contains filtered or unexported fields
}

OrderedPrimaryScanRule matches Sort over FullUnorderedScan and produces a primary scan when the sort keys match the PK columns. For DESC, a reverse primary scan is produced.

Sort([pk ASC/DESC]) over FullUnorderedScan
  → Scan(reverse=DESC)

Complements OrderedIndexScanRule which handles secondary indexes.

func NewOrderedPrimaryScanRule

func NewOrderedPrimaryScanRule() *OrderedPrimaryScanRule

func (*OrderedPrimaryScanRule) Matcher

func (*OrderedPrimaryScanRule) OnMatch

func (r *OrderedPrimaryScanRule) OnMatch(call *ExpressionRuleCall)

type OrderingComparisonCensus

type OrderingComparisonCensus struct {
	// Total is every comparison the site made.
	Total int64

	// FieldPairs is the comparisons where BOTH operands are plain FieldValues —
	// the type class identity decides.
	FieldPairs int64

	// FieldPairsDecided is the FieldPairs where both operands stated an
	// identity, so SameOrderingColumn had a domain and an ordinal on each side.
	FieldPairsDecided int64

	// DeclineResidual is the FieldPairs where at least one operand stated NO
	// identity. Type dispatch declines these, so this is the count that must be
	// ZERO: a nonzero value is a producer minting an ordering key without the
	// layout its ordinal indexes, and the fix belongs there, not here.
	DeclineResidual int64

	// ResidualWeakerArmsAgreed is the DeclineResidual pairs the retired
	// availability dispatch would ALSO have called unequal — a decline that
	// costs nothing.
	ResidualWeakerArmsAgreed int64

	// ResidualMatchesLost is the DeclineResidual pairs the retired availability
	// dispatch would have called EQUAL. Each one is a merge type dispatch gives
	// up, so this is the count whose zero makes the conversion free.
	ResidualMatchesLost int64

	// NonFieldPairs is the comparisons outside the FieldValue class — the
	// *RecordTypeValue discriminators the intersection carries, arithmetic and
	// function ordering keys, the CardinalityValue wrapper.
	NonFieldPairs int64

	// NonFieldBridgeOnly is the NonFieldPairs the ordinal-free NAME bridge
	// decides EQUAL and structural equality does not. It exists to answer
	// whether the bridge arm is still load-bearing once the FieldValue class no
	// longer reaches it; zero means it is measured dead code.
	NonFieldBridgeOnly int64
}

OrderingComparisonCensus is one site's decision population.

func OrderingComparisonCensusOf

func OrderingComparisonCensusOf(site OrderingComparisonSite) OrderingComparisonCensus

OrderingComparisonCensusOf returns a snapshot of one site's counts.

type OrderingComparisonSite

type OrderingComparisonSite int

OrderingComparisonSite identifies one of the two ordering-value comparators.

const (
	// OrderingSiteRequestedVsCandidate is orderingValuesEqualIn: a requested
	// ordering key against a match candidate's ordering key.
	OrderingSiteRequestedVsCandidate OrderingComparisonSite = iota

	// OrderingSiteIntersectionKeys is intersectionValuesEqualIn: the primary-key
	// intersection's own comparison-key, equality-bound and implicit
	// discriminator lists.
	OrderingSiteIntersectionKeys
)

func (OrderingComparisonSite) String

func (s OrderingComparisonSite) String() string

String names the site for test failure messages.

type OrderingPartsComputer

type OrderingPartsComputer interface {
	// ComputeMatchedOrderingParts computes matched ordering parts from
	// this candidate's structure, the existing match info, the sort
	// parameter IDs, and the reverse flag. Returns a list of
	// MatchedOrderingParts describing the order of the outgoing data
	// stream.
	ComputeMatchedOrderingParts(
		matchInfo MatchInfo,
		sortParameterIDs []values.CorrelationIdentifier,
		isReverse bool,
	) []*MatchedOrderingPart
}

OrderingPartsComputer is an optional interface that MatchCandidate implementations can satisfy to provide ordering-part computation for MatchableSortExpression adjustment. It is optional rather than part of the MatchCandidate interface because only order-providing candidates implement it (ValueIndexScanMatchCandidate today; the caller below falls back for the rest).

Ports the computeMatchedOrderingParts method from Java's MatchCandidate / ValueIndexLikeMatchCandidate.

type PartialMatch

type PartialMatch interface {
	GetMatchCandidate() MatchCandidate
	GetMatchInfo() MatchInfo
	GetBoundAliasMap() *AliasMap
	GetQueryRef() *expressions.Reference
	GetQueryExpression() expressions.RelationalExpression
	GetCandidateRef() *expressions.Reference
	GetRegularMatchInfo() *RegularMatchInfo
}

PartialMatch is forward-declared -- full definition will live in partial_match.go. Using an interface to avoid circular dependencies.

func GetPartialMatchesForCandidate

func GetPartialMatchesForCandidate(
	ref *expressions.Reference,
	candidate MatchCandidate,
) []PartialMatch

GetPartialMatchesForCandidate returns all PartialMatches stored on the Reference for the given MatchCandidate. Typed wrapper around Reference.GetPartialMatchesFor. Mirrors Java's Reference.getPartialMatchesForCandidate.

func GetPartialMatchesForExpression

func GetPartialMatchesForExpression(
	ref *expressions.Reference,
	expr expressions.RelationalExpression,
) []PartialMatch

GetPartialMatchesForExpression returns all PartialMatches stored on the Reference whose query expression matches the given expression (identity comparison, matching Java's == check in Reference.getPartialMatchesForExpression).

type PartialMatchImpl

type PartialMatchImpl struct {
	// contains filtered or unexported fields
}

PartialMatchImpl is the concrete implementation of PartialMatch. Links a query-side Reference/Expression to a candidate-side Reference via MatchInfo, establishing that the query subgraph rooted at queryRef is result-equivalent to the candidate subgraph rooted at candidateRef under the bindings in boundAliasMap (modulo compensation).

Ports Java's com.apple.foundationdb.record.query.plan.cascades.PartialMatch.

func NewPartialMatch

func NewPartialMatch(
	boundAliasMap *AliasMap,
	matchCandidate MatchCandidate,
	queryRef *expressions.Reference,
	queryExpression expressions.RelationalExpression,
	candidateRef *expressions.Reference,
	matchInfo MatchInfo,
) *PartialMatchImpl

NewPartialMatch constructs a PartialMatchImpl with all six core fields. Mirrors Java's PartialMatch constructor.

func (*PartialMatchImpl) CompensateCompleteMatch

func (p *PartialMatchImpl) CompensateCompleteMatch(
	unificationPullUp *PullUp,
	candidateTopAlias values.CorrelationIdentifier,
) Compensation

CompensateCompleteMatch computes compensation for a complete match, at the top of a match tree. The bound-parameter prefix map computed here is the one the WHOLE tree compensates against — the parameters belong to the match candidate, not to any single level — so it is threaded down unchanged.

Ports Java's PartialMatch.compensateCompleteMatch.

func (*PartialMatchImpl) CompensateExistential

func (p *PartialMatchImpl) CompensateExistential(
	boundParameterPrefixMap map[values.CorrelationIdentifier]*predicates.ComparisonRange,
) Compensation

CompensateExistential computes the child compensation used by an owning ExistentialValuePredicate. Unlike complete-match compensation, Java starts a fresh match pull-up for this existential child and forwards the candidate's already-computed bound prefix unchanged. The caller cares only whether this child still needs filtering; result-only compensation does not change the truth of EXISTS.

func (*PartialMatchImpl) CompensationCanBeDeferred

func (p *PartialMatchImpl) CompensationCanBeDeferred() bool

CompensationCanBeDeferred reports whether compensation for this match can be deferred to a higher level. Returns false if any unmatched quantifier is ForEach (affects cardinality). Ports Java's PartialMatch.compensationCanBeDeferred().

func (*PartialMatchImpl) GetBoundAliasMap

func (p *PartialMatchImpl) GetBoundAliasMap() *AliasMap

GetBoundAliasMap returns the alias map of all bound correlated references. Mirrors Java's PartialMatch.getBoundAliasMap().

func (*PartialMatchImpl) GetBoundParameterPrefixMap

func (p *PartialMatchImpl) GetBoundParameterPrefixMap() map[values.CorrelationIdentifier]*predicates.ComparisonRange

GetBoundParameterPrefixMap returns the candidate-defined subset of bound parameters that its physical scan can actually satisfy — NOT the whole binding map. For ordinary ordered scans that subset is a leading prefix; specialized candidates may retain independent index-only bindings. Ports Java's PartialMatch.getBoundParameterPrefixMap → MatchCandidate. computeBoundParameterPrefixMap (MatchCandidate.java:147-180).

The distinction is load-bearing: a scan binds an index prefix, so a parameter bound out of order (or after a gap) is NOT usable by the scan and must stay a residual the compensation re-applies. Returning the full map claimed bindings the scan never performs — under-compensating (the dropped predicate silently stops filtering) and over-counting bound parameters wherever the count drives ranking.

Which bindings form a usable prefix is the CANDIDATE's decision, not a generic rule, so this delegates rather than walking the sargable aliases itself. A value index consumes a contiguous equality run and may end on one inequality; a vector index keeps its DistanceRank binding even across a partial partition prefix and refuses partition inequalities outright. Every other consumer of a prefix map already goes through the candidate, and a second, generic implementation here would silently disagree with the scan that ultimately gets built.

func (*PartialMatchImpl) GetBoundSargableAliases

func (p *PartialMatchImpl) GetBoundSargableAliases() map[values.CorrelationIdentifier]struct{}

GetBoundSargableAliases returns the sargable aliases that have non-empty parameter bindings. Ports Java's PartialMatch.getBoundSargableAliases.

func (*PartialMatchImpl) GetCandidateRef

func (p *PartialMatchImpl) GetCandidateRef() *expressions.Reference

GetCandidateRef returns the expression reference on the match candidate side. Mirrors Java's PartialMatch.getCandidateRef().

func (*PartialMatchImpl) GetCompensatedAliases

func (p *PartialMatchImpl) GetCompensatedAliases() map[values.CorrelationIdentifier]struct{}

GetCompensatedAliases returns the set of quantifier aliases that this partial match compensates for. Ports Java's PartialMatch.computeCompensatedAliases (PartialMatch.java:284-305): the matched quantifiers' aliases PLUS, for every query predicate the match mapped, that predicate's correlations which are OWNED by the query expression (i.e. name one of its own quantifiers).

The predicate-owned half matters because a mapped predicate can reference a local quantifier that is not itself a matched quantifier (an existential carried by an EXISTS predicate is the common case). Omitting those aliases under-reports what the match covers, so a consumer intersecting or composing matches can conclude an alias is still free and double-apply or drop the compensation that owns it.

func (*PartialMatchImpl) GetMatchCandidate

func (p *PartialMatchImpl) GetMatchCandidate() MatchCandidate

GetMatchCandidate returns the match candidate this partial match was established against. Satisfies the PartialMatch interface.

func (*PartialMatchImpl) GetMatchInfo

func (p *PartialMatchImpl) GetMatchInfo() MatchInfo

GetMatchInfo returns the match information. Satisfies the PartialMatch interface.

func (*PartialMatchImpl) GetMatchedQuantifiers

func (p *PartialMatchImpl) GetMatchedQuantifiers() []expressions.Quantifier

GetMatchedQuantifiers returns the query expression's quantifiers that have child partial matches in the match info. Ports Java's PartialMatch.getMatchedQuantifiers().

func (*PartialMatchImpl) GetQueryExpression

func (p *PartialMatchImpl) GetQueryExpression() expressions.RelationalExpression

GetQueryExpression returns the expression on the query graph side. Mirrors Java's PartialMatch.getQueryExpression().

func (*PartialMatchImpl) GetQueryRef

func (p *PartialMatchImpl) GetQueryRef() *expressions.Reference

GetQueryRef returns the expression reference on the query graph side. Mirrors Java's PartialMatch.getQueryRef().

func (*PartialMatchImpl) GetRegularMatchInfo

func (p *PartialMatchImpl) GetRegularMatchInfo() *RegularMatchInfo

GetRegularMatchInfo delegates to matchInfo.GetRegularMatchInfo(). Mirrors Java's PartialMatch.getRegularMatchInfo().

func (*PartialMatchImpl) GetUnmatchedQuantifiers

func (p *PartialMatchImpl) GetUnmatchedQuantifiers() []expressions.Quantifier

GetUnmatchedQuantifiers returns the query expression's quantifiers that do NOT have child partial matches. Ports Java's PartialMatch.getUnmatchedQuantifiers().

func (*PartialMatchImpl) PullUp

func (p *PartialMatchImpl) PullUp(candidateAlias values.CorrelationIdentifier) *PullUp

PullUp computes the PullUp chain for this partial match from the candidate side, by nesting through the candidate expression hierarchy. Returns nil when the chain cannot be built faithfully (see NestPullUp).

Ports Java's PartialMatch.pullUp(candidateAlias), which is defined as nestPullUp(null, candidateAlias).getRight().

func (*PartialMatchImpl) String

func (p *PartialMatchImpl) String() string

String returns "ExprTypeName[CandidateName]", mirroring Java's PartialMatch.toString(). Uses the Go type name of the query expression (without package prefix) as the expression type name.

type PartitionBinarySelectRule

type PartitionBinarySelectRule struct {
	// contains filtered or unexported fields
}

PartitionBinarySelectRule handles the special case of a SelectExpression with exactly 2 quantifiers. It rearranges predicates so that each predicate is evaluated at its leftmost possible position, creating sub-SelectExpressions that absorb predicates that can be pushed down.

For each ordering (left, right) and (right, left), the rule attempts to push predicates toward the "left" side (which will become the outer of a nested loop join). Join predicates (referencing both sides) go to the right side (inner), since the left must be planned first. This creates a correlation from right to left, forcing the right side to be planned as the inner of a nested-loop join.

Three cases:

  1. One leg is correlated to the other — the correlated-to leg must be planned as the outer. The rule only produces the valid ordering.
  2. No correlation but join predicates — predicates are pushed to one side, creating a new correlation. Both orderings are explored.
  3. Completely independent — predicates go to whichever side they correlate to. Both orderings are explored (producing equivalent results).

The rule fires twice per pair of quantifiers (once for each assignment of left/right), since it matches quantifiers via exactlyInAnyOrder. Go implements this by iterating over both orderings explicitly.

Ports Java's PartitionBinarySelectRule (ExplorationCascadesRule).

func NewPartitionBinarySelectRule

func NewPartitionBinarySelectRule() *PartitionBinarySelectRule

func (*PartitionBinarySelectRule) Matcher

func (*PartitionBinarySelectRule) OnMatch

type PartitionSelectRule

type PartitionSelectRule struct {
	// contains filtered or unexported fields
}

PartitionSelectRule splits a SelectExpression with N >= 3 quantifiers into two levels: a lower SelectExpression containing a subset of the quantifiers (the "lower" partition) and an upper SelectExpression containing the remaining quantifiers plus a new ForEach quantifier over the lower Select. Predicates are classified by their correlations and distributed to the level where they can be evaluated earliest.

This is the core of join enumeration in the Cascades optimizer: if a query has FROM a, b, c WHERE a.x = b.x AND b.y = c.y, this rule partitions the quantifiers into connected components (a,b and b,c share predicates so they form one component — or the rule explores every possible bipartition and lets cost decide).

The rule fires once per distinct bipartition of the quantifier set. Each firing produces at most one yield — the upper SelectExpression. Convergence is guaranteed because each yielded expression has strictly fewer quantifiers at the top level than the input.

Ports Java's PartitionSelectRule (ExplorationCascadesRule).

func NewPartitionSelectRule

func NewPartitionSelectRule() *PartitionSelectRule

func (*PartitionSelectRule) Matcher

func (*PartitionSelectRule) OnMatch

func (r *PartitionSelectRule) OnMatch(call *ExpressionRuleCall)

type PlanContext

type PlanContext interface {
	// GetPlannerConfiguration returns the planner's configuration —
	// flag bag that controls per-feature planning behaviour (use
	// covering indexes, allow filter-pushdown, etc.).
	GetPlannerConfiguration() PlannerConfiguration

	// GetMatchCandidates returns the set of match candidates available
	// for index-pushdown rules — one per index plus the primary scan,
	// built by plan_context_builder.go from the store's metadata.
	// EmptyPlanContext returns none (rule unit tests).
	GetMatchCandidates() []MatchCandidate

	// GetPrimaryKeyColumns returns the primary key column names for
	// a given record type. Returns nil if the record type has no
	// explicit PK (defaults to synthetic PK).
	GetPrimaryKeyColumns(recordType string) []string
}

PlanContext is the planner's context object — passed to every CascadesRule.OnMatch invocation, holds the static metadata about a record store the rule needs (planner config, available match candidates such as indexes, etc.).

Ports the surface of Java's `com.apple.foundationdb.record.query.plan.cascades.PlanContext`: PlannerConfiguration mirrors the consulted subset of Java's `RecordQueryPlannerConfiguration`, and MatchCandidate the per-candidate (typically per-index) metadata.

func EmptyPlanContext

func EmptyPlanContext() PlanContext

EmptyPlanContext returns the no-info PlanContext singleton. Equivalent to Java's `PlanContext.emptyContext()`.

func NewPlanContextFromIndexDefs

func NewPlanContextFromIndexDefs(defs []IndexDef) PlanContext

NewPlanContextFromIndexDefs builds a PlanContext with one ValueIndexScanMatchCandidate per index definition.

Column names pass through VERBATIM. They are PHYSICAL names — the index definition names proto fields — and the candidate's expansion resolves them by exact name against a base type whose slots are named by values.FieldNameForProtoField, i.e. by the same descriptor. Folding either side breaks that identity for every metadata whose descriptor names are not already upper, and it breaks it SILENTLY: the field request misses, expandValueIndex returns nil, the candidate declines, and the planner full-scans a query whose rows are still correct.

func NewPlanContextFromMatchCandidates

func NewPlanContextFromMatchCandidates(candidates []MatchCandidate) PlanContext

NewPlanContextFromMatchCandidates builds a PlanContext from pre-built MatchCandidates. Use this when you have a mix of ValueIndexScan and AggregateIndex candidates.

func WithCostModelDiagnostics

func WithCostModelDiagnostics(ctx PlanContext, logger *slog.Logger) PlanContext

WithCostModelDiagnostics returns a PlanContext that routes cost-model classification warnings to logger. Warnings are deduplicated once per (walk, concrete plan type) within the returned context, so concurrent and repeated comparisons stay quiet without one diagnostic wrapper suppressing a separately constructed wrapper. Reusing a wrapper intentionally reuses its dedupe scope. A nil logger explicitly disables diagnostics, including any sink carried by an already-wrapped context. A nil context is treated as EmptyPlanContext. Apply this after any other PlanContext decorators so the diagnostic wrapper remains outermost; the private sink is intentionally not added to the public PlanContext interface.

type PlanPartition

type PlanPartition struct {
	// contains filtered or unexported fields
}

PlanPartition groups physical-plan wrapper expressions that share common partitioning property values. Ports Java's PlanPartition.

func FilterPlanPartitions

func FilterPlanPartitions(partitions []*PlanPartition, pred func(*PlanPartition) bool) []*PlanPartition

FilterPlanPartitions returns partitions that satisfy the predicate. Ports Java's PlanPartitionMatchers.filterPlanPartitions.

func NewPlanPartition

func NewPlanPartition(
	partitionProps properties.PropertyMap,
	exprProps map[expressions.RelationalExpression]properties.PropertyMap,
) *PlanPartition

NewPlanPartition creates a partition from property maps.

NOT the production path — toPartitionsFromMap is, and it feeds addExpression from pm.Expressions(), an ordered slice, so orderedExprs is deterministic there. This constructor iterates its input MAP, so the order it records is Go's randomized map-iteration order. Harmless while nothing in the planner calls it; a real nondeterminism source the moment something does, since GetExpressions hands that order to rules that enumerate alternatives from it.

func RollUpPlanPartitions

func RollUpPlanPartitions(partitions []*PlanPartition, interestingProps ...*properties.ExpressionProperty) []*PlanPartition

RollUpPlanPartitions merges partitions by retaining only the specified interesting properties as partition keys.

func SelectMinCostPartition

func SelectMinCostPartition(partitions []*PlanPartition) *PlanPartition

SelectMinCostPartition returns the partition whose first expression has the lowest estimated cost. Ties broken by first occurrence. Returns nil if partitions is empty. Ports Java's ExpressionPartitionMatchers.argmin (simplified).

func ToPlanPartitions

func ToPlanPartitions(ref *expressions.Reference) []*PlanPartition

ToPlanPartitions computes plan partitions for a Reference by reading the pre-computed PlanPropertiesMap (set during PLANNING phase).

A RULE MUST NOT READ A PROPERTY OFF A PARTITION THIS RETURNS. The partition key here is (DistinctRecords, StoredRecord, orderingHash) — see toPartitionsFromMap — so a partition is homogeneous in those three and in NOTHING else, while its partitionProps carries every property copied from whichever member happened to create it. Reading any other property off it is a fact about one member asserted of all of them.

That is a DISTINCT defect from the single-member pick (a rule choosing one member where it should yield over many). This one is the opposite shape: the group is correctly enumerated and then DESCRIBED by one arbitrary element. The two need different fixes, and grepping for one will not find the other — a pick reads like a chosen winner, a read like `[0]` or a `break` on the first match over a member collection.

The gate is roll-up: call RollUpPlanPartitions with an EXPLICIT list of the properties the rule reads, then read only those, off the partition. Roll-up groups by each expression's own value for those properties, so the resulting partition-level value describes every member by construction. Java needs no such discipline because its partition key is the full property set (PlanPartitions), which is why porting a Java rule that reads getPartitionPropertyValue without also porting its rollUpTo silently drops the guarantee the read depends on.

ImplementInUnionRule now rolls the raw partitions up by PropRichOrdering before deriving merge keys, so an equality-bound access cannot share a representative with an unbounded directional scan. It also pins a single member (pinOrderedSpine), and that pin compensates for a SEPARATE defect one layer down: MemoizeFinalExpressionsFromOther (implementation_rule.go:124-151) mints a fresh Reference without a constraint entry, so OptimizeGroupTask's per-ordering retention (unified_tasks.go:663-666) looks up the new reference, finds nothing, and resolves by cost alone. Roll-up alone therefore does not make dropping the pin safe — measured: it yields an InUnion claiming ASC over a filtered full scan.

func WhereDistinct

func WhereDistinct(partitions []*PlanPartition) []*PlanPartition

WhereDistinct returns partitions where DistinctRecords is true.

func WhereOrdered

func WhereOrdered(partitions []*PlanPartition) []*PlanPartition

WhereOrdered returns partitions that have a known ordering.

func WhereStored

func WhereStored(partitions []*PlanPartition) []*PlanPartition

WhereStored returns partitions where StoredRecord is true.

func (*PlanPartition) GetExpressionPropertyValue

func (p *PlanPartition) GetExpressionPropertyValue(
	expr expressions.RelationalExpression,
	prop *properties.ExpressionProperty,
) any

GetExpressionPropertyValue returns a per-expression property value.

func (*PlanPartition) GetExpressions

func (p *PlanPartition) GetExpressions() []expressions.RelationalExpression

GetExpressions returns the wrapper expressions in this partition.

func (*PlanPartition) GetOrdering

func (p *PlanPartition) GetOrdering() properties.Ordering

GetOrdering returns the Ordering from the first expression in this partition. Ordering IS a partitioning dimension — the partition key folds orderingPartitionHash — so members share an ordering hash; the first expression's Ordering is representative. For the precise per-expression value, use GetExpressionPropertyValue.

func (*PlanPartition) GetPartitionPropertiesMap

func (p *PlanPartition) GetPartitionPropertiesMap() properties.PropertyMap

GetPartitionPropertiesMap returns the full partitioning property map.

func (*PlanPartition) GetPartitionPropertyValue

func (p *PlanPartition) GetPartitionPropertyValue(prop *properties.ExpressionProperty) any

GetPartitionPropertyValue returns the partitioning property value for partitioning properties (DistinctRecords, StoredRecord). For non-partitioning properties (Ordering, PrimaryKey), returns the value from the first expression.

func (*PlanPartition) GetPhysicalExpressions

func (p *PlanPartition) GetPhysicalExpressions() []expressions.RelationalExpression

GetPhysicalExpressions returns the PHYSICAL members in GetExpressions order — the expression-side twin of GetPlans, applying the identical filter so the two ARE index-aligned:

exprs := p.GetPhysicalExpressions()
plns  := p.GetPlans()
// exprs[i] is the expression whose plan is plns[i]

This exists because GetExpressions and GetPlans are NOT aligned (see GetPlans). A caller needing an expression together with its plan — the IN-join rule seeds a memo reference from one and a plan chain from the other — must pair them through this rather than by indexing GetExpressions.

func (*PlanPartition) GetPlans

func (p *PlanPartition) GetPlans() []plans.RecordQueryPlan

GetPlans returns the underlying RecordQueryPlans of the PHYSICAL members, in GetExpressions order.

It does NOT index-align with GetExpressions, and used to claim it did ("plans[i] corresponds to exprs[i]"). It skips any non-physical member, so one such member shifts every later index and silently pairs a plan with the wrong expression. Callers that need the pairing must use GetPhysicalExpressions, which applies the identical filter — that pair IS aligned by construction.

The stale claim mattered: rule_implement_in_join.go seeds its reference from GetExpressions and its plan from GetPlans, which is exactly the mispairing this enables (RFC-183 §12).

func (*PlanPartition) HasPrimaryKey

func (p *PlanPartition) HasPrimaryKey() bool

HasPrimaryKey returns true if ANY expression in this partition has a non-nil PrimaryKey property. Per-expression property — not a partitioning dimension.

func (*PlanPartition) IsDistinct

func (p *PlanPartition) IsDistinct() bool

IsDistinct returns true if the partition's DistinctRecords is true.

func (*PlanPartition) IsStoredRecord

func (p *PlanPartition) IsStoredRecord() bool

IsStoredRecord returns true if the partition's StoredRecord is true.

type PlanPropertiesMap

type PlanPropertiesMap struct {
	// contains filtered or unexported fields
}

PlanPropertiesMap stores computed property values for each physical-plan wrapper expression in a Reference's final members.

func GetRefPlanPropertiesMap

func GetRefPlanPropertiesMap(ref *expressions.Reference) *PlanPropertiesMap

GetRefPlanPropertiesMap retrieves the PlanPropertiesMap from a Reference, or nil if not yet computed.

func NewPlanPropertiesMap

func NewPlanPropertiesMap() *PlanPropertiesMap

NewPlanPropertiesMap creates a new empty properties map.

func (*PlanPropertiesMap) Add

func (m *PlanPropertiesMap) Add(w physicalPlanExpression)

Add computes and stores properties for the given physical wrapper.

func (*PlanPropertiesMap) All

All returns the full underlying map. Callers that need deterministic iteration should use Expressions() and GetProperties() instead.

func (*PlanPropertiesMap) Expressions

Expressions returns all wrapper expressions in insertion order.

func (*PlanPropertiesMap) GetProperties

GetProperties returns the computed properties for a wrapper expression.

func (*PlanPropertiesMap) Set

Set stores an ALREADY-COMPUTED property map for an expression, preserving insertion order. Add is the computing twin; this one exists for carrying a source reference's entries into a reference restricted to a subset of its members (MemoizeMemberPlansFromOther), where recomputing would be both wasteful and a second, independently-derived answer to a question the source already answered.

type PlanRebuildError

type PlanRebuildError struct{ Cause error }

PlanRebuildError marks a failure to rebuild an expression with fresh child quantifiers during plan extraction — an arity mismatch between an expression and the children the memo produced for it. Like a yield-invariant violation it is memo corruption, never a statement about the user's query.

It exists so a caller can identify the FAMILY without parsing the message, which the SQL layer withholds because it names Go types.

func (*PlanRebuildError) Error

func (e *PlanRebuildError) Error() string

Error returns the underlying rebuild message unchanged.

func (*PlanRebuildError) Unwrap

func (e *PlanRebuildError) Unwrap() error

Unwrap returns the underlying rebuild error.

type Planner

type Planner struct {

	// MaxTasks caps the total tasks executed before the planner
	// gives up: PlanWithContext returns nil and ErrPlannerCapHit (no
	// partial result — matching Java's throw). Defaults to 100_000. Hitting
	// the cap is a strong signal of a non-terminating rule — callers
	// should report.
	MaxTasks int

	// MaxTaskQueueSize caps the task stack's depth; 0 disables (Java
	// RecordQueryPlannerConfiguration.getMaxTaskQueueSize default). Hitting
	// it returns ErrPlannerQueueCapHit — Java throws
	// RecordQueryPlanComplexityException("Maximum task queue size...").
	MaxTaskQueueSize int

	// MaxNumMatchesPerRuleCall caps the bindings one rule invocation may
	// produce; 0 disables (Java default). Hitting it returns
	// ErrPlannerRuleMatchCapHit via the task-level capErr channel.
	MaxNumMatchesPerRuleCall int

	// DisabledRules holds rule names excluded from selection — Java's
	// PlannerRuleSet filtering via configuration.isRuleEnabled(rule).
	// nil/empty = all rules enabled.
	//
	// Names are the SIMPLE type name ("PredicatePushDownRule"), not the %T
	// spelling ("*cascades.PredicatePushDownRule"): Java keys the same set by
	// `rule.getClass().getSimpleName()`, so this is the one spelling a
	// DISABLED_PLANNER_RULES value can carry across both engines, and it is
	// already the rule registry's key. An unrecognized name is ignored rather
	// than rejected — also Java's behaviour
	// (setDisabledTransformationRuleNames copies the strings verbatim and
	// never resolves them against the rule set).
	DisabledRules map[string]struct{}
	// contains filtered or unexported fields
}

Planner is the task-stack driven cascades planner.

The Java equivalent is `CascadesPlanner` — a task-stack driver over two phases (REWRITING → PLANNING) that explores the expression DAG bottom-up (leaves fire rules first, ancestors after), tracks per-Reference exploration rounds for convergence, and fires rules at per-(group, expression, rule) task granularity. PlanWithContext drives both phases and extracts the cost-cheapest plan via ExtractBestPlanFromSelector.

Convergence: a Reference whose member set stops growing is committed (Reference.CommitExploration); the stack drains; the planner returns. A hard cap (MaxTasks) prevents pathological non-termination from rule-yielding-fresh-members loops; default 100_000.

A Planner is single-use for non-nil planning runs. PlanWithContext(ctx, nil) remains a zero-work no-op; the first non-nil call claims the Planner and every later non-nil call fails with ErrPlannerAlreadyUsed, including a retry after an error or cancellation. Planning mutates both Planner-owned state and the entire Reference DAG, so a fresh Planner is required for every attempt. Configuration methods must therefore be called before the non-nil planning call.

The planning run itself is single-threaded (Java's is too). The one-way claim is atomic so two accidental concurrent callers cannot both enter the stateful task driver.

func NewPlanner

func NewPlanner(rules []ExpressionRule, ctx PlanContext) *Planner

NewPlanner builds a planner with the given rule set + context. Pass DefaultExpressionRules() for the standard rule set.

Pass nil ctx to use the empty PlanContext.

func (*Planner) BestMember

BestMember returns the OPTIMIZE-chosen best member for `ref`, or nil if the Reference wasn't optimized.

func (*Planner) ExtractionVerification

func (p *Planner) ExtractionVerification() ExtractionVerificationReport

ExtractionVerification returns a defensive copy of the report from the last planning run for which verification was enabled.

func (*Planner) HasBestMember

func (p *Planner) HasBestMember(ref *expressions.Reference) bool

HasBestMember reports whether a winner exists for `ref`.

func (*Planner) MaxObservedExplorationRounds

func (p *Planner) MaxObservedExplorationRounds() int

MaxObservedExplorationRounds reports the maximum exploration rounds any Reference started during planning — round-cap retirement evidence (RFC-181 WS-P).

func (*Planner) Memo

func (p *Planner) Memo() *Memo

Memo returns the planner's Memo structure. Available after PlanWithContext has been called (returns nil before that).

func (*Planner) OrderedChildWinner

OrderedChildWinner returns the cheapest physical member of childRef whose derived rich ordering satisfies sortExpr's keys, or nil when none does (the sort must then be materialised). Implements the sort-elision hook of ExtractBestPlanFromSelector: satisfaction runs on the full Value + four-state sort-order representation (RichOrdering.Satisfies), so an ASC_NULLS_LAST sort is never elided against a natural-order ASC scan.

func (*Planner) OrderingSourceRef

func (p *Planner) OrderingSourceRef(expr expressions.RelationalExpression) (*expressions.Reference, bool)

OrderingSourceRef reports whether expr is an order-PRESERVING wrapper (orderingDelegator) and returns the child group its ordering flows from. Implements the second half of the sort-elision seam: extraction must pin that group to a satisfying member when it elides a sort — rebuilding through the group's overall winner could relink the spine to a cheaper UNORDERED member after the sort is already gone.

func (*Planner) PlanWithContext

func (p *Planner) PlanWithContext(ctx context.Context, rootRef *expressions.Reference) (expressions.RelationalExpression, int, error)

PlanWithContext runs the unified two-phase REWRITING → PLANNING pipeline under ctx and returns the cost-cheapest extracted plan tree. It is the only planning entry point production code can reach; the context-less variant is declared in a test file so cancellation and deadlines always have a caller-scoped context to travel on.

func (*Planner) RewritingCoherenceViolations

func (p *Planner) RewritingCoherenceViolations() []string

RewritingCoherenceViolations returns what the coherence check recorded across the last planning run, when enabled. Empty means every REWRITING winner stamped by OptimizeGroup was identical to the group's designated final at stamp time — costing and pruning agreed everywhere.

func (*Planner) SetReachabilityCollector

func (p *Planner) SetReachabilityCollector(c *ReachabilityCollector)

SetReachabilityCollector points this planner's yield-time reachability accounting at c. nil (the default) collects nothing.

There is no global "enable" switch by design. Turning collection on used to mean flipping a package bool, which enabled it for every planner in the process — including ones belonging to unrelated concurrent tests, whose yields then landed in the caller's tally. Enabling is now inseparable from naming the destination.

func (*Planner) SetVerifyExtractionUnambiguous

func (p *Planner) SetVerifyExtractionUnambiguous(on bool)

SetVerifyExtractionUnambiguous enables RFC-224's post-drain extraction-path verification for this planner. It is off by default because it performs an additional traversal of the selected memo path.

func (*Planner) SetVerifyRewritingCoherence

func (p *Planner) SetVerifyRewritingCoherence(on bool)

SetVerifyRewritingCoherence turns the RFC-186 REWRITING coherence check on for this planner. The embedded plan harness enables it permanently — a violation there is a red test, not a log line.

func (*Planner) Statistics

func (p *Planner) Statistics() properties.StatisticsProvider

Statistics returns the planner's statistics provider, or nil if none set.

func (*Planner) TieBrokenCostLess

func (p *Planner) TieBrokenCostLess(stats properties.StatisticsProvider) func(a, b expressions.RelationalExpression) bool

TieBrokenCostLess supplies extraction's fallback GetBest with a TOTAL-ORDER comparator (TieBrokenCostSelector): the scalar cost comparator ties routinely and GetBest would otherwise resolve by member insertion order, flipping picks across plannings.

func (*Planner) WithCostModel

func (p *Planner) WithCostModel(less func(a, b expressions.RelationalExpression) bool) *Planner

WithCostModel sets the comparator used by OptimizeGroupTask. Use RewritingCostModelLess for the REWRITING phase, PlanningCostModelLess for the PLANNING phase. Matches Java's per-phase cost model. Returns p.

func (*Planner) WithImplementationRules

func (p *Planner) WithImplementationRules(rules []ImplementationRule) *Planner

WithImplementationRules adds rules for PhasePlanning. These run after the REWRITING phase converges. Returns p for chaining.

func (*Planner) WithMaxTasks

func (p *Planner) WithMaxTasks(n int) *Planner

WithMaxTasks overrides the task cap. Returns p for chaining.

func (*Planner) WithPlanningExpressionRules

func (p *Planner) WithPlanningExpressionRules(rules []ExpressionRule) *Planner

WithPlanningExpressionRules adds ExpressionRules that fire during PLANNING's bottom-up implementation pass. Unlike EXPLORE-phase expression rules (which yield to members via Insert), these yield to finalMembers via InsertFinal: physical scan/filter/agg wrappers are produced during PLANNING, where constraint and ordering information is available. Production passes BatchAExpressionRules.

func (*Planner) WithStatistics

func (p *Planner) WithStatistics(stats properties.StatisticsProvider) *Planner

WithStatistics sets the table-level cardinality statistics for the cost model. Stats flow through EstimateCost and HintCost to give scan/index wrappers real cardinality instead of the default 1e6 constant. Replaces the cost model — call after WithCostModel if both are used.

type PlannerBudgetExceededError

type PlannerBudgetExceededError struct {
	// Budget names the configured bound, matching the Planner field.
	Budget string
	// Limit is the configured bound.
	Limit int
	// Observed is the value that exceeded it.
	Observed int
	// contains filtered or unexported fields
}

PlannerBudgetExceededError reports WHICH planning budget was exhausted and how far the run got before it tripped. Java attaches the same pair to its RecordQueryPlanComplexityException via addLogInfo (MAX_TASK_COUNT/TASK_COUNT and the queue and rule-match equivalents), and the SQL layer propagates them as error context; without the numbers a user cannot tell a near-miss from a query that is orders of magnitude past the bound.

Error and Unwrap both delegate to the cap sentinel, so every existing errors.Is check and every message assertion keeps working unchanged — this type only adds the numbers.

func (*PlannerBudgetExceededError) Error

Error returns the cap sentinel's message.

func (*PlannerBudgetExceededError) Unwrap

func (e *PlannerBudgetExceededError) Unwrap() error

Unwrap returns the cap sentinel so errors.Is identifies which cap tripped.

type PlannerConfiguration

type PlannerConfiguration struct {
	// AllowDuplicateProjections — when true, a projection can carry
	// the same Value twice (a SQL planner concession; the executor
	// handles the duplicate emission). False by default; matches
	// Java's `RecordQueryPlannerConfiguration.allowDuplicateProjections`.
	AllowDuplicateProjections bool

	// AttemptFailedInJoinAsUnionMaxSize controls when InUnionRule falls
	// back from InJoin to InUnion. Java default is 0 (no fallback).
	AttemptFailedInJoinAsUnionMaxSize int

	// SingleReadVersion asserts that the WHOLE result of this statement is
	// produced at ONE read version, rather than each page taking a fresh one.
	//
	// It exists because a cross-row uniqueness proof is a statement about an
	// INSTANT. A secondary UNIQUE index guarantees at most one row per key
	// value at any single read version; a paged auto-commit statement spans
	// many, and between two pages a value may be deleted from one row and
	// re-inserted on another with a higher key. The index is never violated,
	// and the value is emitted twice.
	//
	// False is the fail-closed default: a planning run that has not stated the
	// condition does not get proofs that depend on it.
	SingleReadVersion bool

	// ShouldJoinRightDeep — when true, PartitionSelectRule only
	// produces right-deep join trees (the upper partition has exactly
	// 1 quantifier). This reduces join enumeration combinatorics at the
	// cost of excluding bushy plans. Mirrors Java's
	// RecordQueryPlannerConfiguration.shouldJoinRightDeep().
	ShouldJoinRightDeep bool

	// ShouldDeferCrossProducts — when true, PartitionSelectRule only
	// partitions along independent quantifier boundaries (cross-product
	// splits). Partitions that would split connected components are
	// deferred until the components are individually partitioned into
	// smaller pieces. Mirrors Java's
	// RecordQueryPlannerConfiguration.shouldDeferCrossProducts().
	ShouldDeferCrossProducts bool

	// IndexScanPreference mirrors Java's
	// RecordQueryPlannerConfiguration.getIndexScanPreference() (proto enum
	// PREFER_SCAN=0 / PREFER_INDEX=1 / PREFER_PRIMARY_KEY_INDEX=2). Consulted by
	// the cost model's primary-vs-index tie-break (comparePrimaryScanToIndexScan).
	// Zero value = PreferScan, the Cascades default; no SQL surface sets a
	// non-default today (same as the sibling knobs), so this changes no reachable
	// plan — it models the mechanism for parity.
	IndexScanPreference IndexScanPreference

	// ReadableIndexes is the planner's view of which indexes may be scanned,
	// the port of Java's `PlannerConfiguration.readableIndexes`
	// (fdb-relational-core .../query/PlannerConfiguration.java:54-70, an
	// `Optional<Set<String>>`).
	//
	// Java derives it in PlanContext.Builder.getReadableIndexes
	// (PlanContext.java:236-247): when the store state reports every index
	// readable it is absent, and otherwise it is the set of readable,
	// non-universal index names. It reaches the core planner through
	// CascadesPlanner.planGraph and is applied in
	// MetaDataPlanContext.forRootReference (MetaDataPlanContext.java:194-199)
	// as `indexList.removeIf(index -> !allowedIndexes.contains(index.getName()))`
	// — a filter on the INDEX LIST, before any match candidate is created.
	//
	// The zero value is UNKNOWN: it permits every index to back a scan, so a
	// construction site that never learned about index states plans exactly as
	// it did before, but it proves nothing about index state.
	ReadableIndexes ReadableIndexes
}

PlannerConfiguration mirrors the subset of Java's `RecordQueryPlannerConfiguration` that rules actually consult. As further config-driven rules port, fields land here in step with their consumers.

func DefaultPlannerConfiguration

func DefaultPlannerConfiguration() PlannerConfiguration

DefaultPlannerConfiguration mirrors Java's `RecordQueryPlannerConfiguration.defaultPlannerConfiguration()`.

type PlannerConstraint

type PlannerConstraint[T any] struct {
	// contains filtered or unexported fields
}

PlannerConstraint is a typed key for constraints that flow between rules during the PLANNING phase. Rules read constraints set by their parent and push constraints to child References.

Ports Java's PlannerConstraint.

type PlannerInvariantViolationError

type PlannerInvariantViolationError struct{ Cause error }

PlannerInvariantViolationError marks a structural invariant violation caught at rule-yield time: a nil expression, a quantifier over an unmemoized child, or a nil-inner shell. Every such failure is an engine bug, never a statement about the user's query.

It exists so a caller can identify the FAMILY without parsing the message. The underlying text names Go types (it formats the offending expression with %T), so the SQL layer withholds it from users; a family that survives as a type is what keeps those failures triageable from a log line.

func (*PlannerInvariantViolationError) Error

Error returns the underlying invariant message unchanged, so in-package diagnostics and tests are unaffected by the tagging.

func (*PlannerInvariantViolationError) Unwrap

Unwrap returns the underlying invariant error.

type PlannerPhase

type PlannerPhase int

PlannerPhase drives the multi-phase planning lifecycle. Java's CascadesPlanner runs REWRITING first (exploration rules, RewritingCostModel), then PLANNING (implementation rules, PlanningCostModel). Each phase has its own rule set and cost model.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.PlannerPhase.

const (
	PhaseRewriting PlannerPhase = iota
	PhasePlanning
)

func (PlannerPhase) HasNextPhase

func (p PlannerPhase) HasNextPhase() bool

HasNextPhase reports whether there is a subsequent phase.

func (PlannerPhase) NextPhase

func (p PlannerPhase) NextPhase() PlannerPhase

NextPhase returns the phase that follows this one. PhasePlanning is terminal (panics if called).

func (PlannerPhase) String

func (p PlannerPhase) String() string

func (PlannerPhase) TargetStage

func (p PlannerPhase) TargetStage() expressions.PlannerStage

TargetStage returns the PlannerStage that References should reach after this phase completes. Mirrors Java's PlannerPhase.getTargetPlannerStage().

type PredicateCompensation

type PredicateCompensation func(
	partialMatch PartialMatch,
	boundParameterPrefixMap map[values.CorrelationIdentifier]*predicates.ComparisonRange,
	pullUp *PullUp,
) PredicateCompensationFunc

PredicateCompensation is the functional interface for computing a PredicateCompensationFunction from a partial match context. Ports Java's PredicateMultiMap.PredicateCompensation.

func DefaultPredicateCompensation

func DefaultPredicateCompensation() PredicateCompensation

DefaultPredicateCompensation returns a PredicateCompensation that always yields no compensation needed. This is the default used by Java's PredicateMapping.Builder.

type PredicateCompensationFunc

type PredicateCompensationFunc interface {
	IsNeeded() bool
	IsImpossible() bool

	// Amend recreates the compensation function with updated aggregate
	// value mappings. Used during intersection of compensations when
	// aggregates are finalized. Returns a new function (or self if
	// no change needed).
	//
	// unmatchedAggregateMap: BiMap of unmatched aggregate aliases → values
	// amendedMatchedAggregateMap: mapping of old values → new values
	//
	// Ports Java's PredicateCompensationFunction.amend.
	Amend(
		unmatchedAggregateMap *BiMap[values.CorrelationIdentifier, values.Value],
		amendedMatchedAggregateMap map[values.Value]values.Value,
	) PredicateCompensationFunc

	// ApplyCompensationForPredicate applies this compensation by
	// translating correlation references via the translation map and
	// returning the set of predicates that must be injected above the
	// matched candidate scan.
	//
	// Ports Java's PredicateCompensationFunction.applyCompensationForPredicate.
	//
	// The bool reports whether the compensation could be expressed at all. A
	// false is NOT an empty compensation: dropping a residual predicate that
	// could not be translated returns rows the query excludes, so the caller
	// must abandon the match rather than proceed with what it did get.
	ApplyCompensationForPredicate(translationMap TranslationMap) ([]predicates.QueryPredicate, bool)
}

PredicateCompensationFunc represents the result of computing predicate compensation — whether compensation is needed, impossible, or what predicates to apply. Ports Java's PredicateMultiMap.PredicateCompensationFunction.

func ImpossiblePredicateCompensation

func ImpossiblePredicateCompensation() PredicateCompensationFunc

ImpossiblePredicateCompensation returns a PredicateCompensationFunc indicating compensation is needed but impossible.

func NoPredicateCompensationNeeded

func NoPredicateCompensationNeeded() PredicateCompensationFunc

NoPredicateCompensationNeeded returns a PredicateCompensationFunc indicating no compensation is required.

func OfExistentialValuePredicateCompensation

func OfExistentialValuePredicateCompensation(
	predicate *predicates.ExistentialValuePredicate,
) PredicateCompensationFunc

OfExistentialValuePredicateCompensation creates Java's specialized EVP compensation function. A malformed nil predicate fails closed.

func OfPredicateCompensation

func OfPredicateCompensation(pred predicates.QueryPredicate, shouldSimplifyValues bool) PredicateCompensationFunc

OfPredicateCompensation creates a PredicateCompensationFunc that wraps a query predicate. When applied, it translates the predicate through the translation map and returns it for injection as a residual filter. Ports Java's PredicateCompensationFunction.ofPredicate.

type PredicateCompensationMap

type PredicateCompensationMap struct {
	// contains filtered or unexported fields
}

PredicateCompensationMap maps query predicates to compensation functions using identity-based keying (pointer equality). Ports Java's LinkedIdentityMap<QueryPredicate, PredicateCompensationFunction>.

func EmptyPredicateCompensationMap

func EmptyPredicateCompensationMap() *PredicateCompensationMap

EmptyPredicateCompensationMap returns an empty predicate compensation map.

func NewPredicateCompensationMap

func NewPredicateCompensationMap(keys []predicates.QueryPredicate, vals []PredicateCompensationFunc) *PredicateCompensationMap

NewPredicateCompensationMap creates a PredicateCompensationMap from parallel slices of predicates and compensation functions.

func StubPredicateCompensationMap

func StubPredicateCompensationMap(n int) *PredicateCompensationMap

StubPredicateCompensationMap creates a PredicateCompensationMap with N no-op entries. Used by tests that need a non-empty map to drive IsNeeded/IsNeededForFiltering without real predicate content.

func (*PredicateCompensationMap) Amend

func (m *PredicateCompensationMap) Amend(
	unmatchedAggregateMap *BiMap[values.CorrelationIdentifier, values.Value],
	amendedMatchedAggregateMap map[values.Value]values.Value,
) *PredicateCompensationMap

Amend creates a new PredicateCompensationMap with all compensation functions amended. Ports the amend loop in Java's Compensation.ForMatch.intersect.

func (*PredicateCompensationMap) ApplyCompensations

func (m *PredicateCompensationMap) ApplyCompensations(tm TranslationMap) ([]predicates.QueryPredicate, bool)

ApplyCompensations applies all compensation functions in this map via the given translation map and returns the collected residual predicates. Ports the iteration in Java's ForMatch.apply(). The bool is false when any single compensation could not be expressed; see PredicateCompensationFunc.ApplyCompensationForPredicate for why a partial answer is not an acceptable substitute.

func (*PredicateCompensationMap) Entries

Entries returns the predicate→compensation pairs in insertion order.

func (*PredicateCompensationMap) Get

Get returns the compensation function for the given predicate key using identity (pointer) comparison. Returns nil if not found. Mirrors Java's LinkedIdentityMap.get().

func (*PredicateCompensationMap) IsEmpty

func (m *PredicateCompensationMap) IsEmpty() bool

IsEmpty reports whether the map has no entries.

func (*PredicateCompensationMap) Len

func (m *PredicateCompensationMap) Len() int

Len returns the number of entries in the map.

type PredicateMap

type PredicateMap struct {
	PredicateMultiMap
}

PredicateMap is a PredicateMultiMap that enforces the constraint that each query predicate maps to at most one candidate predicate mapping. Ports Java's PredicateMap.

func EmptyPredicateMap

func EmptyPredicateMap() *PredicateMap

EmptyPredicateMap returns an empty PredicateMap.

func (*PredicateMap) GetMappingOptional

func (m *PredicateMap) GetMappingOptional(pred predicates.QueryPredicate) (*PredicateMapping, bool)

GetMappingOptional returns the single mapping for the given predicate, or nil/false if the predicate has no mapping or has more than one. Mirrors Java's PredicateMap.getMappingOptional().

type PredicateMapBuilder

type PredicateMapBuilder struct {
	PredicateMultiMapBuilder
}

PredicateMapBuilder builds a PredicateMap, enforcing uniqueness. Ports Java's PredicateMap.Builder.

func NewPredicateMapBuilder

func NewPredicateMapBuilder() *PredicateMapBuilder

NewPredicateMapBuilder creates a new empty PredicateMapBuilder.

func (*PredicateMapBuilder) Build

func (b *PredicateMapBuilder) Build() *PredicateMap

Build constructs the PredicateMap. Panics if there are conflicts or non-unique mappings.

func (*PredicateMapBuilder) BuildMaybe

func (b *PredicateMapBuilder) BuildMaybe() *PredicateMap

BuildMaybe constructs the PredicateMap, returning nil if there are conflicts or non-unique mappings.

type PredicateMapping

type PredicateMapping struct {
	// contains filtered or unexported fields
}

PredicateMapping maps a query predicate to a candidate predicate along with associated compensation, parameter binding, comparison range, constraint, and translated predicate.

Ports Java's PredicateMultiMap.PredicateMapping.

func (*PredicateMapping) GetCandidatePredicate

func (m *PredicateMapping) GetCandidatePredicate() predicates.QueryPredicate

GetCandidatePredicate returns the candidate predicate.

func (*PredicateMapping) GetComparisonRange

func (m *PredicateMapping) GetComparisonRange() *predicates.ComparisonRange

GetComparisonRange returns the comparison range, or nil if none.

func (*PredicateMapping) GetConstraint

func (m *PredicateMapping) GetConstraint() *QueryPlanConstraint

GetConstraint returns the query plan constraint.

func (*PredicateMapping) GetMappingKey

func (m *PredicateMapping) GetMappingKey() MappingKey

GetMappingKey returns the full mapping key.

func (*PredicateMapping) GetMappingKind

func (m *PredicateMapping) GetMappingKind() MappingKind

GetMappingKind returns the mapping kind.

func (*PredicateMapping) GetOriginalQueryPredicate

func (m *PredicateMapping) GetOriginalQueryPredicate() predicates.QueryPredicate

GetOriginalQueryPredicate returns the original query predicate.

func (*PredicateMapping) GetParameterAlias

func (m *PredicateMapping) GetParameterAlias() *values.CorrelationIdentifier

GetParameterAlias returns the parameter alias, or nil if none.

func (*PredicateMapping) GetPredicateCompensation

func (m *PredicateMapping) GetPredicateCompensation() PredicateCompensation

GetPredicateCompensation returns the predicate compensation function.

func (*PredicateMapping) GetTranslatedQueryPredicate

func (m *PredicateMapping) GetTranslatedQueryPredicate() predicates.QueryPredicate

GetTranslatedQueryPredicate returns the translated query predicate.

func (*PredicateMapping) ToBuilder

func (m *PredicateMapping) ToBuilder() *PredicateMappingBuilder

ToBuilder returns a PredicateMappingBuilder pre-seeded with this mapping's values. Mirrors Java's PredicateMapping.toBuilder().

func (*PredicateMapping) WithTranslatedQueryPredicate

func (m *PredicateMapping) WithTranslatedQueryPredicate(translated predicates.QueryPredicate) *PredicateMapping

WithTranslatedQueryPredicate returns a copy of this mapping with the translated query predicate replaced. Mirrors Java's PredicateMapping.withTranslatedQueryPredicate().

type PredicateMappingBuilder

type PredicateMappingBuilder struct {
	// contains filtered or unexported fields
}

PredicateMappingBuilder builds a PredicateMapping. Ports Java's PredicateMultiMap.PredicateMapping.Builder.

func OrTermMappingBuilder

func OrTermMappingBuilder(
	originalQueryPredicate predicates.QueryPredicate,
	translatedQueryPredicate predicates.QueryPredicate,
	candidatePredicate predicates.QueryPredicate,
) *PredicateMappingBuilder

OrTermMappingBuilder creates a PredicateMappingBuilder for an OR-term mapping. Mirrors Java's PredicateMapping.orTermMappingBuilder().

func RegularMappingBuilder

func RegularMappingBuilder(
	originalQueryPredicate predicates.QueryPredicate,
	translatedQueryPredicate predicates.QueryPredicate,
	candidatePredicate predicates.QueryPredicate,
) *PredicateMappingBuilder

RegularMappingBuilder creates a PredicateMappingBuilder for a regular (non-OR-term) mapping. Mirrors Java's PredicateMapping.regularMappingBuilder().

func (*PredicateMappingBuilder) Build

Build constructs the PredicateMapping.

func (*PredicateMappingBuilder) SetComparisonRange

SetComparisonRange sets the comparison range.

func (*PredicateMappingBuilder) SetConstraint

SetConstraint sets the query plan constraint.

func (*PredicateMappingBuilder) SetParameterAlias

SetParameterAlias sets the parameter alias.

func (*PredicateMappingBuilder) SetPredicateCompensation

SetPredicateCompensation sets the predicate compensation function.

func (*PredicateMappingBuilder) SetSargable

SetSargable sets both the parameter alias and comparison range. Mirrors Java's Builder.setSargable().

func (*PredicateMappingBuilder) SetTranslatedQueryPredicate

func (b *PredicateMappingBuilder) SetTranslatedQueryPredicate(p predicates.QueryPredicate) *PredicateMappingBuilder

SetTranslatedQueryPredicate sets the translated query predicate.

type PredicateMultiMap

type PredicateMultiMap struct {
	// contains filtered or unexported fields
}

PredicateMultiMap maps query predicates to sets of candidate predicate mappings. Uses identity-based keying (pointer equality) for predicates, matching Java's LinkedIdentityMap semantics.

The zero value is a valid, empty PredicateMultiMap.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.PredicateMultiMap.

func (*PredicateMultiMap) Entries

func (m *PredicateMultiMap) Entries() []struct {
	Predicate predicates.QueryPredicate
	Mapping   *PredicateMapping
}

Entries returns all (predicate, mapping) pairs in insertion order. Each predicate may appear once; each entry may have multiple mappings.

func (*PredicateMultiMap) Get

Get returns the mappings for the given query predicate, or nil if the predicate is not in the map. Uses identity (pointer) equality.

func (*PredicateMultiMap) KeySet

KeySet returns the distinct query predicates in insertion order.

func (*PredicateMultiMap) PredicateCount

func (m *PredicateMultiMap) PredicateCount() int

PredicateCount returns the number of distinct predicates in the map.

func (*PredicateMultiMap) Size

func (m *PredicateMultiMap) Size() int

Size returns the total number of mappings across all predicates.

func (*PredicateMultiMap) Values

func (m *PredicateMultiMap) Values() []*PredicateMapping

Values returns all mappings across all predicates, in insertion order.

type PredicateMultiMapBuilder

type PredicateMultiMapBuilder struct {
	// contains filtered or unexported fields
}

PredicateMultiMapBuilder builds a PredicateMultiMap. Ports Java's PredicateMultiMap.Builder.

func NewPredicateMultiMapBuilder

func NewPredicateMultiMapBuilder() *PredicateMultiMapBuilder

NewPredicateMultiMapBuilder creates a new empty builder.

func (*PredicateMultiMapBuilder) Build

Build constructs the PredicateMultiMap. Panics if there are conflicts (a candidate predicate mapped from multiple query predicates). Mirrors Java's Builder.build().

func (*PredicateMultiMapBuilder) BuildMaybe

BuildMaybe constructs the PredicateMultiMap, returning nil if there are conflicts. Mirrors Java's Builder.buildMaybe().

func (*PredicateMultiMapBuilder) Put

func (b *PredicateMultiMapBuilder) Put(
	queryPredicate predicates.QueryPredicate,
	mapping *PredicateMapping,
) bool

Put adds a mapping for the given query predicate. Returns true if the mapping was actually added (not a duplicate).

func (*PredicateMultiMapBuilder) PutAll

PutAll adds all entries from an existing PredicateMultiMap. Returns true if any mapping was added.

func (*PredicateMultiMapBuilder) PutAllMappings

func (b *PredicateMultiMapBuilder) PutAllMappings(
	queryPredicate predicates.QueryPredicate,
	mappings []*PredicateMapping,
) bool

PutAllMappings adds all mappings for a single query predicate. Returns true if any mapping was added.

type PredicatePushDownRule

type PredicatePushDownRule struct {
	// contains filtered or unexported fields
}

PredicatePushDownRule pushes predicates from an outer SelectExpression into its child quantifiers when the predicate only references a single child's aliases. This is the generic predicate push-down rule intended for the REWRITING phase — it handles arbitrary SelectExpression shapes, unlike the specific Push*Through* rules which target Filter/Projection/Sort/etc.

Algorithm:

  1. Match SelectExpression.
  2. For each ForEach quantifier, identify predicates that can be pushed — those whose correlation set has no overlap with any OTHER quantifier's alias.
  3. For each pushable-predicate + quantifier pair, visit the child expression through the quantifier's Reference and create a new expression with the predicate absorbed or pushed through.
  4. Build a new outer SelectExpression with the remaining (non-pushed) predicates and the rewritten quantifiers.

Existential quantifiers are handled correctly: the per-quantifier loop skips non-ForEach quantifiers, so predicates are only pushed into ForEach children. Existential siblings remain untouched. Matches Java's behavior (no global existential guard).

Convergence: each firing strictly reduces the set of pushable predicates in the outer SelectExpression. A SelectExpression with no pushable predicates causes zero yields.

Ports Java's PredicatePushDownRule (ExplorationCascadesRule, 444 LOC).

func NewPredicatePushDownRule

func NewPredicatePushDownRule() *PredicatePushDownRule

func (*PredicatePushDownRule) Matcher

func (*PredicatePushDownRule) OnMatch

func (r *PredicatePushDownRule) OnMatch(call *ExpressionRuleCall)

type PredicateToLogicalUnionRule

type PredicateToLogicalUnionRule struct {
	// contains filtered or unexported fields
}

PredicateToLogicalUnionRule transforms a SelectExpression whose predicates (in CNF after NormalizePredicatesRule) contain OR terms into a UNIQUE(UNION(SELECT leg1, SELECT leg2, ...)) structure. Each union leg corresponds to one DNF term, enabling each leg to use a different index for evaluation.

The core transformation:

SELECT WHERE A AND B AND (C1 OR C2) AND (D1 OR D2)

becomes:

UNIQUE-REQUIRED(UNION(
  UNIQUE(SELECT WHERE A AND B AND C1 AND D1),
  UNIQUE(SELECT WHERE A AND B AND C1 AND D2),
  UNIQUE(SELECT WHERE A AND B AND C2 AND D1),
  UNIQUE(SELECT WHERE A AND B AND C2 AND D2),
))

Both dedup levels are BY PRIMARY KEY: per-leg, and again across legs, where a record satisfying several DNF terms arrives once per leg that produced it. The fixed predicates (A, B) are repeated in every leg.

The outer dedup corresponds to Java's LogicalDistinctExpression, which is a primary-key dedup; Go's node of that name is a full-row dedup and would be the wrong one. See the construction site below for why the distinction decides whether this rewrite returns duplicate records.

Guards:

  • Only fires on SelectExpressions with exactly 1 ForEach quantifier.
  • Skips SelectExpressions with Existential quantifiers.
  • Requires at least one non-leaf, non-atomic OR predicate.
  • Respects DefaultMaxNumConjuncts to avoid combinatorial explosion.

Convergence: the output is Unique(Union(...)), not a SelectExpression, so the rule cannot re-fire on its own output.

Ports Java's PredicateToLogicalUnionRule (a match-partition rule) as a Go ExpressionRule operating on SelectExpressions. The Go planner fires expression rules in the EXPLORE phase rather than as match-partition triggers — architecturally equivalent when combined with NormalizePredicatesRule.

func NewPredicateToLogicalUnionRule

func NewPredicateToLogicalUnionRule() *PredicateToLogicalUnionRule

NewPredicateToLogicalUnionRule constructs the rule.

func (*PredicateToLogicalUnionRule) Matcher

func (*PredicateToLogicalUnionRule) OnMatch

type PrimaryScanMatchCandidate

type PrimaryScanMatchCandidate struct {
	// contains filtered or unexported fields
}

PrimaryScanMatchCandidate represents a match candidate backed by the primary key scan. The primary scan is always unique (the PK uniquely identifies every record).

In Java, PrimaryScanMatchCandidate implements MatchCandidate, ValueIndexLikeMatchCandidate, and WithPrimaryKeyMatchCandidate. The Go port implements MatchCandidate, WithPrimaryKeyMatchCandidate, WithBaseQuantifierMatchCandidate, and OrderingPartsComputer — the last of these standing in for the ValueIndexLikeMatchCandidate default that gives a Java primary scan its key order.

Key distinction from ValueIndexScanMatchCandidate: the primary scan has separate "available" vs "queried" record types. Available types are all types in the store; queried types are the subset the query needs. Whether a TypeFilterPlan wraps the scan depends on HasAndOrderedByRecordTypeKey(), not on whether available and queried differ — see ToScanPlan.

func NewPrimaryScanMatchCandidate

func NewPrimaryScanMatchCandidate(
	traversal *Traversal,
	parameters []values.CorrelationIdentifier,
	availableRecordTypes []string,
	queriedRecordTypes []string,
	primaryKeyColumns []string,
	hasRecordTypeKeyPrefix bool,
	baseType values.Type,
) *PrimaryScanMatchCandidate

NewPrimaryScanMatchCandidate constructs a primary-scan match candidate.

  • traversal: the Traversal of this candidate's expression tree (may be nil if not yet computed; GetTraversal will build it lazily).
  • parameters: sargable aliases, one per PK column.
  • availableRecordTypes: all record types in the store.
  • queriedRecordTypes: the subset of types the query needs.
  • primaryKeyColumns: the PK column names in order.
  • hasRecordTypeKeyPrefix: whether the primary key backing this candidate starts with a RecordTypeKeyExpression — see the field doc on hasRecordTypeKeyPrefix. Callers derive this from RecordType.PrimaryKeyHasRecordTypePrefix() / RecordMetaData.PrimaryKeyHasRecordTypePrefix().
  • baseType: the record type of the scan output.

func (*PrimaryScanMatchCandidate) CandidateName

func (c *PrimaryScanMatchCandidate) CandidateName() string

CandidateName returns "primary(type1,type2,...)" using the available record types. Mirrors Java's PrimaryScanMatchCandidate.getName().

func (*PrimaryScanMatchCandidate) ComputeBoundParameterPrefixMap

ComputeBoundParameterPrefixMap walks the sargable aliases in order and collects the longest prefix satisfying index scan discipline: N equalities + optional trailing inequality.

Identical to ValueIndexScanMatchCandidate.ComputeBoundParameterPrefixMap — mirrors Java's default MatchCandidate.computeBoundParameterPrefixMap.

func (*PrimaryScanMatchCandidate) ComputeMatchedOrderingParts

func (c *PrimaryScanMatchCandidate) ComputeMatchedOrderingParts(
	matchInfo MatchInfo,
	sortParameterIDs []values.CorrelationIdentifier,
	isReverse bool,
) []*MatchedOrderingPart

ComputeMatchedOrderingParts computes one ordering part per primary-key column the sort requests, in key order, carrying whatever comparison range the match bound for that column.

Java's PrimaryScanMatchCandidate inherits this from ValueIndexLikeMatchCandidate, so a primary-scan match reports its key order exactly like an index-scan match. Without it a primary-scan match reports NO ordering, so it can never satisfy a requested ordering and never gets a reverse variant — `WHERE pk = ? ORDER BY pk2 DESC` fell back to an in-memory sort over the forward scan, and no descending IN-union over a primary scan could be enumerated at all.

A primary key is a flat list of scalar columns (no fan-out), so the duplicate-producing branch of Java's shared implementation has nothing to act on here, and there is no trimmed-PK suffix to continue into: the primary key IS the full key.

func (*PrimaryScanMatchCandidate) GetAvailableRecordTypes

func (c *PrimaryScanMatchCandidate) GetAvailableRecordTypes() []string

GetAvailableRecordTypes returns all record types available in the store. Mirrors Java's PrimaryScanMatchCandidate.getAvailableRecordTypes().

func (*PrimaryScanMatchCandidate) GetBaseType

func (c *PrimaryScanMatchCandidate) GetBaseType() values.Type

GetBaseType returns the base record type. Implements WithBaseQuantifierMatchCandidate.

func (*PrimaryScanMatchCandidate) GetColumnNames

func (c *PrimaryScanMatchCandidate) GetColumnNames() []string

GetColumnNames returns the primary key column names.

func (*PrimaryScanMatchCandidate) GetKeyComponentTypes

func (c *PrimaryScanMatchCandidate) GetKeyComponentTypes() []values.Type

GetKeyComponentTypes returns physical types aligned with the candidate's primary-key columns.

func (*PrimaryScanMatchCandidate) GetPrimaryKeyValues

func (c *PrimaryScanMatchCandidate) GetPrimaryKeyValues() []values.Value

GetPrimaryKeyValues returns the primary key as a list of Values. Lazily computed from primaryKeyColumns. Returns nil if there are no PK columns. Implements WithPrimaryKeyMatchCandidate.

func (*PrimaryScanMatchCandidate) GetRecordTypes

func (c *PrimaryScanMatchCandidate) GetRecordTypes() []string

GetRecordTypes returns the queried record type names. This matches Go's MatchCandidate.GetRecordTypes() contract and Java's getQueriedRecordTypeNames().

func (*PrimaryScanMatchCandidate) GetSargableAliases

func (c *PrimaryScanMatchCandidate) GetSargableAliases() []values.CorrelationIdentifier

GetSargableAliases returns the ordered parameter list (one per PK column).

func (*PrimaryScanMatchCandidate) GetTraversal

func (c *PrimaryScanMatchCandidate) GetTraversal() *Traversal

GetTraversal returns the Traversal of this candidate's expression tree, built lazily on first access via ExpandValueIndex. The traversal is stable once computed (sync.Once).

func (*PrimaryScanMatchCandidate) HasAndOrderedByRecordTypeKey

func (c *PrimaryScanMatchCandidate) HasAndOrderedByRecordTypeKey() bool

HasAndOrderedByRecordTypeKey reports whether the primary key starts with the record type key, partitioning the scan by record type. Mirrors Java's PrimaryScanMatchCandidate.hasAndOrderedByRecordTypeKey().

func (*PrimaryScanMatchCandidate) IsUnique

func (c *PrimaryScanMatchCandidate) IsUnique() bool

IsUnique returns true — the primary key is always unique.

func (*PrimaryScanMatchCandidate) String

func (c *PrimaryScanMatchCandidate) String() string

String returns a human-readable label for debugging. Mirrors Java's PrimaryScanMatchCandidate.toString().

func (*PrimaryScanMatchCandidate) ToScanPlan

ToScanPlan converts the matched prefix into a physical plan. When the primary key is NOT ordered by the record-type key (HasAndOrderedByRecordTypeKey() is false), the scan's key range is not type-discriminated and can genuinely produce rows of any available type, so the scan is wrapped in a TypeFilterPlan restricting it to the queried types.

Mirrors Java's PrimaryScanMatchCandidate.toEquivalentPlan(): extracts ComparisonRanges from the prefix map in PK column order and embeds them as ScanComparisons in the RecordQueryScanPlan, then wraps in a TypeFilterPlan unless hasAndOrderedByRecordTypeKey() — a scan whose key range is already prefixed by the record-type ordinal cannot yield any other type, so a filter on top would be pure overhead (and, worse for costing, an unearned selectivity discount; see TypeFilterCost).

func (*PrimaryScanMatchCandidate) WithKeyComponentTypes

func (c *PrimaryScanMatchCandidate) WithKeyComponentTypes(types []values.Type) *PrimaryScanMatchCandidate

WithKeyComponentTypes attaches authoritative physical primary-key types to a freshly constructed candidate.

type PrimaryScanRule

type PrimaryScanRule struct {
	// contains filtered or unexported fields
}

PrimaryScanRule implements a logical FullUnorderedScanExpression as a physical RecordQueryScanPlan.

FullUnorderedScan({records}, type)  →  Scan({records}, type, false)

"Implements" rules in Cascades go from logical to physical. The rule output is YIELDED into the same Reference as the logical input, so the Reference holds BOTH alternatives — the logical and the physical — and downstream rule chains operate on whichever members they pattern-match; cost extraction picks the physical (the logical has no Execute path).

The rule yields a bare `*plans.RecordQueryScanPlan`, which is its own physical Cascades expression (RFC-184 W2).

Java equivalent: `PrimaryScanRule`. This rule is the unindexed fallback; sargable primary-key access goes through PrimaryScanMatchCandidate on the data-access path (primary_scan_match_candidate.go).

func NewPrimaryScanRule

func NewPrimaryScanRule() *PrimaryScanRule

NewPrimaryScanRule constructs the rule.

func (*PrimaryScanRule) Matcher

Matcher returns the pattern.

func (*PrimaryScanRule) OnMatch

func (r *PrimaryScanRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires on every FullUnorderedScanExpression and yields a RecordQueryScanPlan over the same record types.

type ProjectionMergeCensus

type ProjectionMergeCensus struct {
	// RuleFirings is every time OnMatch found a projection over a projection —
	// counted BEFORE any slot is looked at.
	//
	// It is the DENOMINATOR, and it is counted independently rather than summed
	// from the arms below. A denominator built by adding up its own parts is true
	// by construction and detects nothing; this one can disagree with them, which
	// is the only way it can catch a slot loop that stops running.
	//
	// Its other job is to keep the LazyOuterReads zero from being vacuous. A zero
	// over a population nothing reached reads exactly like a zero over a
	// population that was reached and stayed clean, and only this count separates
	// them.
	RuleFirings int64

	// SlotCompositions is every outer projection slot the rule examined, across
	// all firings — including the ones that made it decline.
	SlotCompositions int64

	// BakedSingleAccessor is the slots that TOOK the ordinal arm: the outer read
	// carried a resolved single-accessor path, so it named the inner's output
	// slot structurally. This is the arm that carries all real traffic.
	//
	// "Took the arm", not "was composed" — the increment precedes the
	// ordinal-range check, so a baked read whose ordinal falls outside the
	// inner's slot list is counted here and then declines. That placement is
	// deliberate: counting after the range check would leave such a slot in no
	// arm at all, and the arms-sum cross-check below is worth more than the
	// finer distinction. The population it names is "reads that arrived with an
	// ordinal", which is exactly the population the LazyOuterReads zero is the
	// complement of.
	BakedSingleAccessor int64

	// LazyOuterReads is the slots where the outer read was a childless FieldValue
	// with NO resolved path — a display name and nothing else.
	//
	// This is the population the retired name-matching arm served, and it MUST be
	// ZERO. The resolver bakes a projection-output reference to its output ordinal
	// before it reaches this rule; a nonzero value means that stopped happening,
	// and the fix belongs there, not here. The rule's own behaviour on such a read
	// is a fail-closed decline either way — it will never select a slot by name
	// again — so a regression costs a lost merge, never a wrong column.
	LazyOuterReads int64

	// DeclinedNotComposable is the slots that were neither: a non-FieldValue, a
	// read with a live child, or a resolved path with more than one accessor.
	//
	// It is counted so the three arms can be checked against SlotCompositions.
	// Without it a slot vanishing from the loop would look like a clean zero.
	DeclinedNotComposable int64
}

ProjectionMergeCensus is the population of outer slots ProjectionMergeRule examined.

func ProjectionMergeCensusSnapshot

func ProjectionMergeCensusSnapshot() ProjectionMergeCensus

ProjectionMergeCensusSnapshot returns the current counts.

type ProjectionMergeFloors

type ProjectionMergeFloors struct {
	RuleFirings         int64
	BakedSingleAccessor int64
}

ProjectionMergeFloors is the population floor for one corpus.

ORDER-OF-MAGNITUDE below the measurement, like every other per-site floor on this path: it exists to catch the rule going dark, not to re-bless a count that moves whenever a test file is added. The load-bearing assertion is the LazyOuterReads ZERO, which is not configurable and is checked unconditionally.

type ProjectionMergeRule

type ProjectionMergeRule struct {
	// contains filtered or unexported fields
}

ProjectionMergeRule collapses two nested LogicalProjections into one by COMPOSING the outer list through the inner:

Projection(P1) over Projection(P2) over X
→
Projection(P1 ∘ P2) over X

Each outer slot that reads inner output k is substituted with P2[k] (baked-ordinal reads by ordinal; a lazy read carries no ordinal and declines). The pre-composition version kept P1 verbatim on the name-era contract that "the projection list is a side channel; rows pass through" — FALSE under the ordinal model, where P1's reads are baked against P2's OUTPUT row: dropping P2 reshaped the row underneath the baked ordinals (see OnMatch). A shape composition cannot prove declines the merge — both projections simply remain.

This is more aggressive than Java's planner (which lets the cost model + cardinality estimates decide): no dedicated Java rule exists — the Memo's cost preference for fewer Projection operators emerges from physical plan choice (a fetch-only plan with the wider column list is cheaper than two stacked Projection operators). Go gets the same shape via this static rewrite.

func NewProjectionMergeRule

func NewProjectionMergeRule() *ProjectionMergeRule

NewProjectionMergeRule constructs the rule.

func (*ProjectionMergeRule) Matcher

Matcher returns the pattern.

func (*ProjectionMergeRule) OnMatch

func (r *ProjectionMergeRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the inner Quantifier ranges over another LogicalProjection; yields a flat projection wrapping the inner's inner — with the outer's values COMPOSED through the inner's list.

The original rule kept P1 verbatim, on the name-era contract that the projection list was "a side channel; rows pass through". Under the ordinal model that contract is FALSE: P1's values are BAKED by ordinal against the INNER projection's output row, so dropping the inner reshapes the row underneath the baked ordinals. `Project([Y#0], Project([COUNT(*)#1], StreamingAgg(keys=[COL1],…)))` merged to `Project([Y#0], StreamingAgg(…))` — reading the GROUP KEY instead of the count (SUM over a grouped UNION ALL branch returned key-sums; yamsql union_aggregate_java, RFC-180). Composition is the fix the rule's own caveat predicted: [#0]∘[#1] = [#1] — each outer slot k substitutes the inner's projected value at the ordinal it reads. A shape composition cannot prove (nested field path, unresolved lazy name, non-FieldValue outer slot) declines the merge — correctness over operator count; both projections simply remain.

type PullFilterAboveDistinctRule

type PullFilterAboveDistinctRule struct {
	// contains filtered or unexported fields
}

PullFilterAboveDistinctRule pulls a LogicalFilter ABOVE a LogicalDistinct — the inverse direction of PushFilterThroughDistinctRule.

Distinct(Filter(P, X))  →  Filter(P, Distinct(X))

Soundness: same row set either side. Filter narrows by P; Distinct dedupes. Both compositions admit "DISTINCT rows of X passing P".

Why we keep BOTH this AND PushFilterThroughDistinct: the two shapes coexist in the memo as alternatives. Cost-model extraction picks the cheaper. Without cost, both stay; exploration terminates because Reference.Insert's SemanticEquals fallback absorbs structurally-equivalent re-yields after the first round.

Optimization argument: filtering AFTER dedup means evaluating P on fewer rows (Distinct pre-shrinks the set) — the inverse trade-off from PushFilterThroughDistinct. Which is cheaper depends on dedup-vs-filter cost; the cost model picks.

func NewPullFilterAboveDistinctRule

func NewPullFilterAboveDistinctRule() *PullFilterAboveDistinctRule

NewPullFilterAboveDistinctRule constructs the rule.

func (*PullFilterAboveDistinctRule) Matcher

Matcher returns the pattern.

func (*PullFilterAboveDistinctRule) OnMatch

OnMatch fires when the inner Quantifier ranges over a LogicalFilterExpression.

type PullUp

type PullUp struct {
	// contains filtered or unexported fields
}

PullUp tracks how values are translated as matching walks up through expression boundaries. Each PullUp level represents one candidate expression in the match path, carrying the candidate alias and the "pull-through" value (the result value of that expression). The chain is walked bottom-up when pulling up values from a match's inner scope to the top-level candidate scope.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.values.translation.PullUp.

func ForMatch

func ForMatch(
	parent *PullUp,
	candidateAlias values.CorrelationIdentifier,
	candidateExpression expressions.RelationalExpression,
) *PullUp

ForMatch creates a PullUp for the match case by visiting the candidate expression to determine the pull-through value and ranged-over aliases.

Ports Java's PullUp.forMatch + PullUpVisitor.visit.

func ForUnification

func ForUnification(
	candidateAlias values.CorrelationIdentifier,
	pullThroughValue values.Value,
	rangedOverAliases map[values.CorrelationIdentifier]struct{},
) *PullUp

ForUnification creates a PullUp for the unification case (no parent). Ports Java's PullUp.forUnification.

func NewPullUp

func NewPullUp(
	parent *PullUp,
	candidateAlias values.CorrelationIdentifier,
	pullThroughValue values.Value,
	rangedOverAliases map[values.CorrelationIdentifier]struct{},
) *PullUp

NewPullUp constructs a PullUp level.

func (*PullUp) GetCandidateAlias

func (p *PullUp) GetCandidateAlias() values.CorrelationIdentifier

func (*PullUp) GetParent

func (p *PullUp) GetParent() *PullUp

func (*PullUp) GetPullThroughValue

func (p *PullUp) GetPullThroughValue() values.Value

func (*PullUp) GetRangedOverAliases

func (p *PullUp) GetRangedOverAliases() map[values.CorrelationIdentifier]struct{}

func (*PullUp) GetRoot

func (p *PullUp) GetRoot() *PullUp

func (*PullUp) IsMatch

func (p *PullUp) IsMatch() bool

IsMatch reports whether this level was built by ForMatch (Java's PullUp.MatchPullUp) as opposed to ForUnification.

func (*PullUp) IsRoot

func (p *PullUp) IsRoot() bool

func (*PullUp) PullUpValueMaybe

func (p *PullUp) PullUpValueMaybe(v values.Value) values.Value

PullUpValueMaybe translates a Value from the match scope to the top-level candidate scope by walking up the PullUp chain. At each level, it computes a MaxMatchMap between the current value and the pull-through value, then translates via the candidate alias.

Returns nil if the value cannot be pulled up at any level.

Ports Java's PullUp.pullUpValueMaybe.

func (*PullUp) PullUpValueMaybeWithEquivalence

func (p *PullUp) PullUpValueMaybeWithEquivalence(v values.Value, ve ValueEquivalence) values.Value

PullUpValueMaybeWithEquivalence is like PullUpValueMaybe but accepts a ValueEquivalence for cross-alias matching during MaxMatchMap computation. Ports Java's overload that threads ValueEquivalence.

type PushDistinctBelowFilterRule

type PushDistinctBelowFilterRule struct {
	// contains filtered or unexported fields
}

PushDistinctBelowFilterRule moves a physical RecordQueryUnorderedPrimaryKeyDistinctPlan below a RecordQueryPredicatesFilterPlan. This ensures generated plans match the form produced by Java's heuristic planner (distinct below filter).

Pattern:

Distinct(Filter([P], inner))  →  Filter([P'], Distinct(inner))

Predicates P are rebased from the old filter quantifier alias to the new quantifier over the distinct plan.

Mirrors Java's `PushDistinctBelowFilterRule`.

func NewPushDistinctBelowFilterRule

func NewPushDistinctBelowFilterRule() *PushDistinctBelowFilterRule

func (*PushDistinctBelowFilterRule) Matcher

func (*PushDistinctBelowFilterRule) OnMatch

type PushDistinctThroughFetchRule

type PushDistinctThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushDistinctThroughFetchRule pushes a physical RecordQueryUnorderedPrimaryKeyDistinctPlan through a RecordQueryFetchFromPartialRecordPlan to reduce the number of records before the (expensive) fetch.

Pattern:

PrimaryKeyDistinct(Fetch(inner))  →  Fetch(PrimaryKeyDistinct(inner))

The dedup key must be the PRIMARY KEY for this to be sound, and that is why the rule matches the primary-key node and not the full-row one. Below the fetch the rows are PARTIAL records. A fetch is 1:1 from record to row but not injective over partial rows: two partial rows that differ can map to the SAME record, which is exactly what happens when the inner is a union of covering scans over DIFFERENT indexes — one contributes (a, pk), the other (b, a, pk). A full-row dedup then collapses nothing and the record is fetched once per index that produced it, while the same dedup ABOVE the fetch (over whole records) would have collapsed them. The primary key is carried identically by every partial row for a record, so the primary-key dedup is unaffected by where it sits.

Mirrors Java's `PushDistinctThroughFetchRule`, whose root matcher is `unorderedPrimaryKeyDistinctPlan(fetchFromPartialRecordPlan(anyPlan()))`.

func NewPushDistinctThroughFetchRule

func NewPushDistinctThroughFetchRule() *PushDistinctThroughFetchRule

func (*PushDistinctThroughFetchRule) Matcher

func (*PushDistinctThroughFetchRule) OnMatch

type PushFilterBelowJoinRule

type PushFilterBelowJoinRule struct {
	// contains filtered or unexported fields
}

PushFilterBelowJoinRule pushes filter predicates below a join (SelectExpression with 2 ForEach quantifiers) when a predicate references columns from only one side of the join.

Filter([a.name='foo', a.id=b.aid], Select(rv, [qA, qB], [jpreds]))
  → Filter([a.id=b.aid], Select(rv, [qA', qB], [jpreds]))
    where qA' = ForEach(Filter([a.name='foo'], A))

A predicate is pushable to side i when every FieldValue in its tree is qualified with sourceAliases[i] and does not reference sourceAliases[j] (j != i). Predicates that reference both sides, or that have no FieldValue references at all, are kept on the filter above the join (conservative).

The rule only fires on INNER joins (JoinInner). LEFT OUTER and CROSS joins have different NULL-preservation semantics that make predicate pushdown unsound without additional analysis.

Soundness: for inner joins, Filter(P, Join(A, B)) and Join(Filter(P, A), B) produce the same result set when P only references A — the filter's admittance decision is independent of B's rows.

Optimization argument: filtering before the join reduces the number of rows the join processes, which is typically O(n*m) becoming O(filtered_n * m).

func NewPushFilterBelowJoinRule

func NewPushFilterBelowJoinRule() *PushFilterBelowJoinRule

NewPushFilterBelowJoinRule constructs the rule.

func (*PushFilterBelowJoinRule) Matcher

Matcher returns the pattern.

func (*PushFilterBelowJoinRule) OnMatch

func (r *PushFilterBelowJoinRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the pattern matches a LogicalFilterExpression.

type PushFilterThroughDistinctRule

type PushFilterThroughDistinctRule struct {
	// contains filtered or unexported fields
}

PushFilterThroughDistinctRule pushes a LogicalFilter under a LogicalDistinct.

Filter(P, Distinct(X))  →  Distinct(Filter(P, X))

Soundness: the inner Distinct doesn't reshape rows (its GetResultValue is the inner's flowed object value), so FieldValue references inside P resolve against the same row shape on both sides of the rewrite. Output row sets are equal because both forms yield "DISTINCT rows of X that satisfy P".

Optimization argument: filtering BEFORE dedup is faster — the dedup runs over fewer rows. Eliminated rows can never become duplicates after-the-fact.

Termination: the rule yields a Distinct wrapping a Quantifier over a fresh Reference holding the new Filter. The fresh Reference would defeat the sameChildReferences pointer-identity dedup, but Reference.Insert's SemanticEquals fallback (post-680e664a) catches the structural equivalence and absorbs the second yield.

Java equivalent: emerges from cost preference for cheaper plans.

func NewPushFilterThroughDistinctRule

func NewPushFilterThroughDistinctRule() *PushFilterThroughDistinctRule

NewPushFilterThroughDistinctRule constructs the rule.

func (*PushFilterThroughDistinctRule) Matcher

Matcher returns the pattern.

func (*PushFilterThroughDistinctRule) OnMatch

OnMatch fires when the inner Quantifier ranges over a LogicalDistinctExpression.

type PushFilterThroughFetchRule

type PushFilterThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushFilterThroughFetchRule pushes predicates that can be evaluated on partial records (index entries) underneath a FetchFromPartialRecordPlan. This filters out non-matching records before the expensive fetch.

A predicate is pushable iff all its leaf values can be translated from the full-record domain to the partial-record (index) domain via the fetch's TranslateValueFunction.

Three cases:

  1. No predicates pushable → return (no yield)
  2. All predicates pushable → Fetch(Filter(pushed, inner))
  3. Some pushable, some residual → Filter(residual, Fetch(Filter(pushed, inner)))

Mirrors Java's `PushFilterThroughFetchRule`.

func NewPushFilterThroughFetchRule

func NewPushFilterThroughFetchRule() *PushFilterThroughFetchRule

func (*PushFilterThroughFetchRule) Matcher

func (*PushFilterThroughFetchRule) OnMatch

type PushFilterThroughGroupByRule

type PushFilterThroughGroupByRule struct {
	// contains filtered or unexported fields
}

PushFilterThroughGroupByRule pushes predicates from a LogicalFilter below a GroupByExpression when those predicates reference only the grouping keys. Supports partial pushdown: pushable predicates move below GroupBy; residual predicates stay as a filter above.

Filter([P1, P2], GroupBy(keys, aggs, X))
  → Filter([P2], GroupBy(keys, aggs, Filter([P1], X)))  [P1 on keys, P2 not]
  → GroupBy(keys, aggs, Filter([P1, P2], X))            [all on keys]

Soundness: if a predicate references only grouping-key columns, filtering before aggregation produces the same groups — rows eliminated by the predicate wouldn't contribute to any group that survives it.

GO-ONLY EXTENSION — there is no Java counterpart to port, and the name this comment used to cite ("PushPredicateThroughGroupByRule") names no class in 4.12.11.0. Java's general predicate pushdown, PredicatePushDownRule, reaches a GroupBy and deliberately declines:

// We have to be a little careful here. In particular, we can push down any
// predicates on a grouping column, but not any on the aggregate value. For
// now, just don't push anything down
return Optional.empty();
  — PredicatePushDownRule.visitGroupByExpression, PredicatePushDownRule.java:394-399

So Java states this rule's exact soundness condition and then implements neither half. This is a read-side extension: it only lets the planner reach a cheaper plan for a query Java also answers, and nothing about it touches the wire. The comparand check below is the "but not any on the aggregate value" half made real rather than assumed.

Not to be confused with PushRequestedOrderingThroughGroupByRule (PushRequestedOrderingThroughGroupByRule.java:52), which Java does have — it propagates an ordering CONSTRAINT through a GroupBy and rewrites no predicate.

func NewPushFilterThroughGroupByRule

func NewPushFilterThroughGroupByRule() *PushFilterThroughGroupByRule

func (*PushFilterThroughGroupByRule) Matcher

func (*PushFilterThroughGroupByRule) OnMatch

type PushFilterThroughTypeFilterRule

type PushFilterThroughTypeFilterRule struct {
	// contains filtered or unexported fields
}

PushFilterThroughTypeFilterRule pushes a LogicalFilter under a LogicalTypeFilter.

Filter(P, TypeFilter([T], X))  →  TypeFilter([T], Filter(P, X))

Soundness: TypeFilter doesn't reshape rows (its GetResultValue is the inner's flowed object value). FieldValue references inside P resolve against the same row shape on both sides. The TypeFilter admits only rows of the listed types; the Filter admits only rows satisfying P. Composing the two predicates is commutative for row admittance — same output rows.

Optimization argument: TypeFilter is typically cheap (record-type dispatch). Filter is potentially expensive (predicate evaluation). Putting Filter "lower" in the tree gives the downstream data-access / implementation rules a chance to push the predicate INTO the scan itself (e.g. into a covering-index range scan).

func NewPushFilterThroughTypeFilterRule

func NewPushFilterThroughTypeFilterRule() *PushFilterThroughTypeFilterRule

NewPushFilterThroughTypeFilterRule constructs the rule.

func (*PushFilterThroughTypeFilterRule) Matcher

Matcher returns the pattern.

func (*PushFilterThroughTypeFilterRule) OnMatch

OnMatch fires when the inner Quantifier ranges over a LogicalTypeFilterExpression.

type PushInJoinThroughFetchRule

type PushInJoinThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushInJoinThroughFetchRule pushes a RecordQueryInJoinPlan through a FetchFromPartialRecordPlan. The InJoin runs on the partial/index records (cheaper), and the Fetch is lifted to the top.

Before:

InJoin(Fetch(inner))

After:

Fetch(InJoin(inner))

Go collapses Java's 3 InJoin subclasses (InValuesJoin, InParameterJoin, InComparandJoin) into a single RecordQueryInJoinPlan, so only one rule instance is needed (Java instantiates this rule twice).

Mirrors Java's PushInJoinThroughFetchRule.

func NewPushInJoinThroughFetchRule

func NewPushInJoinThroughFetchRule() *PushInJoinThroughFetchRule

func (*PushInJoinThroughFetchRule) Matcher

func (*PushInJoinThroughFetchRule) OnMatch

type PushInUnionThroughFetchRule

type PushInUnionThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushInUnionThroughFetchRule handles the InUnion case. Java: PushSetOperationThroughFetchRule<RecordQueryInUnionOnValuesPlan>.

func NewPushInUnionThroughFetchRule

func NewPushInUnionThroughFetchRule() *PushInUnionThroughFetchRule

func (*PushInUnionThroughFetchRule) Matcher

func (*PushInUnionThroughFetchRule) OnMatch

type PushIntersectionThroughFetchRule

type PushIntersectionThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushIntersectionThroughFetchRule handles the Intersection case. Java: PushSetOperationThroughFetchRule<RecordQueryIntersectionOnValuesPlan>.

func NewPushIntersectionThroughFetchRule

func NewPushIntersectionThroughFetchRule() *PushIntersectionThroughFetchRule

func (*PushIntersectionThroughFetchRule) Matcher

func (*PushIntersectionThroughFetchRule) OnMatch

type PushLimitThroughUnionRule

type PushLimitThroughUnionRule struct {
	// contains filtered or unexported fields
}

PushLimitThroughUnionRule pushes a LIMIT into each branch of a UNION ALL. Each branch needs at most (limit + offset) rows since the outer LIMIT will pick from the union output. The outer LIMIT remains in place — it still needs to enforce the final cardinality.

Pattern:

LogicalLimit(limit, offset)
  inner → LogicalUnion(q1, q2, …)

Rewrite:

LogicalLimit(limit, offset)
  inner → LogicalUnion(
    LIMIT(limit+offset, 0, q1.inner),
    LIMIT(limit+offset, 0, q2.inner),
    …)

The branch limit is limit+offset because the outer LIMIT may skip 'offset' rows before taking 'limit' rows.

func NewPushLimitThroughUnionRule

func NewPushLimitThroughUnionRule() *PushLimitThroughUnionRule

func (*PushLimitThroughUnionRule) Matcher

func (*PushLimitThroughUnionRule) OnMatch

type PushMapThroughFetchRule

type PushMapThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushMapThroughFetchRule pushes a map expression through a FetchFromPartialRecordPlan when all values referenced by the map's result value can be translated to the partial-record (index) domain. This eliminates the fetch entirely — the map runs directly on the covering index scan.

Pattern:

Map(resultValue, Fetch(inner))  →  Map(translatedResultValue, inner)

The fetch is completely eliminated because the map reshapes the output in a way that detaches downstream data flow from the full record.

Mirrors Java's `PushMapThroughFetchRule`.

func NewPushMapThroughFetchRule

func NewPushMapThroughFetchRule() *PushMapThroughFetchRule

func (*PushMapThroughFetchRule) Matcher

func (*PushMapThroughFetchRule) OnMatch

type PushMergeSortUnionThroughFetchRule

type PushMergeSortUnionThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushMergeSortUnionThroughFetchRule handles the ordered merge-sort union — Go's analogue of Java's ordered union. Java: PushSetOperationThroughFetchRule<RecordQueryUnionOnValuesPlan>.

func NewPushMergeSortUnionThroughFetchRule

func NewPushMergeSortUnionThroughFetchRule() *PushMergeSortUnionThroughFetchRule

func (*PushMergeSortUnionThroughFetchRule) Matcher

func (*PushMergeSortUnionThroughFetchRule) OnMatch

type PushReferencedFieldsThroughDistinctRule

type PushReferencedFieldsThroughDistinctRule struct {
	// contains filtered or unexported fields
}

PushReferencedFieldsThroughDistinctRule pushes referenced-field constraints through LogicalDistinctExpression (transparent pass-through).

Ports Java's PushReferencedFieldsThroughDistinctRule.

func NewPushReferencedFieldsThroughDistinctRule

func NewPushReferencedFieldsThroughDistinctRule() *PushReferencedFieldsThroughDistinctRule

func (PushReferencedFieldsThroughDistinctRule) IsPreOrder

func (PushReferencedFieldsThroughDistinctRule) IsPreOrder() bool

func (*PushReferencedFieldsThroughDistinctRule) Matcher

func (*PushReferencedFieldsThroughDistinctRule) OnMatch

type PushReferencedFieldsThroughFilterRule

type PushReferencedFieldsThroughFilterRule struct {
	// contains filtered or unexported fields
}

PushReferencedFieldsThroughFilterRule pushes referenced-field constraints through LogicalFilterExpression. Extracts FieldValues from the filter's predicates, unions them with any incoming constraint, and pushes the result to the child Reference.

Ports Java's PushReferencedFieldsThroughFilterRule.

func NewPushReferencedFieldsThroughFilterRule

func NewPushReferencedFieldsThroughFilterRule() *PushReferencedFieldsThroughFilterRule

func (PushReferencedFieldsThroughFilterRule) IsPreOrder

func (PushReferencedFieldsThroughFilterRule) IsPreOrder() bool

func (*PushReferencedFieldsThroughFilterRule) Matcher

func (*PushReferencedFieldsThroughFilterRule) OnMatch

type PushReferencedFieldsThroughSelectRule

type PushReferencedFieldsThroughSelectRule struct {
	// contains filtered or unexported fields
}

PushReferencedFieldsThroughSelectRule pushes referenced-field constraints through SelectExpression. Extracts FieldValues from predicates and the result value, unions with incoming constraint, and pushes to all child References.

Ports Java's PushReferencedFieldsThroughSelectRule.

func NewPushReferencedFieldsThroughSelectRule

func NewPushReferencedFieldsThroughSelectRule() *PushReferencedFieldsThroughSelectRule

func (PushReferencedFieldsThroughSelectRule) IsPreOrder

func (PushReferencedFieldsThroughSelectRule) IsPreOrder() bool

func (*PushReferencedFieldsThroughSelectRule) Matcher

func (*PushReferencedFieldsThroughSelectRule) OnMatch

type PushReferencedFieldsThroughUniqueRule

type PushReferencedFieldsThroughUniqueRule struct {
	// contains filtered or unexported fields
}

PushReferencedFieldsThroughUniqueRule pushes referenced-field constraints through LogicalUniqueExpression (transparent pass-through).

Ports Java's PushReferencedFieldsThroughUniqueRule.

func NewPushReferencedFieldsThroughUniqueRule

func NewPushReferencedFieldsThroughUniqueRule() *PushReferencedFieldsThroughUniqueRule

func (PushReferencedFieldsThroughUniqueRule) IsPreOrder

func (PushReferencedFieldsThroughUniqueRule) IsPreOrder() bool

func (*PushReferencedFieldsThroughUniqueRule) Matcher

func (*PushReferencedFieldsThroughUniqueRule) OnMatch

type PushRequestedOrderingThroughDeleteRule

type PushRequestedOrderingThroughDeleteRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughDeleteRule is a PLANNING-phase ImplementationRule that propagates a RequestedOrdering constraint through a DeleteExpression. Delete passes through rows unchanged (it only removes them from the store), so the requested ordering passes through unchanged to the child Reference.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op — ImplementDeleteRule handles the actual delete implementation.

Ports Java's PushRequestedOrderingThroughDeleteRule.

func NewPushRequestedOrderingThroughDeleteRule

func NewPushRequestedOrderingThroughDeleteRule() *PushRequestedOrderingThroughDeleteRule

func (PushRequestedOrderingThroughDeleteRule) IsPreOrder

func (PushRequestedOrderingThroughDeleteRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughDeleteRule) Matcher

func (*PushRequestedOrderingThroughDeleteRule) OnMatch

type PushRequestedOrderingThroughDistinctRule

type PushRequestedOrderingThroughDistinctRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughDistinctRule is a PLANNING-phase ImplementationRule that propagates a RequestedOrdering constraint through a LogicalDistinctExpression. Distinct preserves row order, so the requested ordering passes through unchanged to the child Reference.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op — ImplementDistinctFinalRule handles the actual distinct implementation.

Ports Java's PushRequestedOrderingThroughDistinctRule.

func NewPushRequestedOrderingThroughDistinctRule

func NewPushRequestedOrderingThroughDistinctRule() *PushRequestedOrderingThroughDistinctRule

func (PushRequestedOrderingThroughDistinctRule) IsPreOrder

func (PushRequestedOrderingThroughDistinctRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughDistinctRule) Matcher

func (*PushRequestedOrderingThroughDistinctRule) OnMatch

type PushRequestedOrderingThroughFilterRule

type PushRequestedOrderingThroughFilterRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughFilterRule is a PLANNING-phase ImplementationRule that propagates a RequestedOrdering constraint through a LogicalFilterExpression. Filtering removes rows but does not reorder them, so the requested ordering passes through unchanged to the child Reference.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op — ImplementFilterRule / ImplementSimpleSelectRule handle the actual filter implementation.

Ports Java's PushRequestedOrderingThroughSelectRule (for the single-source filter case — Go's LogicalFilterExpression maps to Java's SelectExpression with a single ForEach quantifier and predicates only).

func NewPushRequestedOrderingThroughFilterRule

func NewPushRequestedOrderingThroughFilterRule() *PushRequestedOrderingThroughFilterRule

func (PushRequestedOrderingThroughFilterRule) IsPreOrder

func (PushRequestedOrderingThroughFilterRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughFilterRule) Matcher

func (*PushRequestedOrderingThroughFilterRule) OnMatch

type PushRequestedOrderingThroughGroupByRule

type PushRequestedOrderingThroughGroupByRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughGroupByRule is a PLANNING-phase ImplementationRule that synthesizes compatible orderings from a RequestedOrdering constraint and a GroupByExpression's grouping keys, then pushes the synthesized ordering to the child Reference.

GroupBy is NOT ordering-transparent — the pushed ordering must cover all grouping keys (for streaming aggregation) while respecting the requested ordering's key prefix (for the outer ORDER BY). The rule synthesizes the ordering as:

  • Each requested ordering part that matches a grouping key (by field name, case-insensitive) retains its sort direction.
  • Remaining grouping keys not in the request are appended with ANY sort order (direction doesn't matter for streaming aggregation — they just need to be contiguous).

If any requested ordering part does NOT match a grouping key, the ordering is incompatible and is not pushed. If there are no grouping keys (scalar aggregation), a preserve ordering is pushed — scalar aggregation produces 0-1 rows, so any ordering is trivially satisfied.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op.

Ports Java's PushRequestedOrderingThroughGroupByRule.

func NewPushRequestedOrderingThroughGroupByRule

func NewPushRequestedOrderingThroughGroupByRule() *PushRequestedOrderingThroughGroupByRule

func (PushRequestedOrderingThroughGroupByRule) IsPreOrder

func (PushRequestedOrderingThroughGroupByRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughGroupByRule) Matcher

func (*PushRequestedOrderingThroughGroupByRule) OnMatch

type PushRequestedOrderingThroughInLikeSelectRule

type PushRequestedOrderingThroughInLikeSelectRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughInLikeSelectRule is a PLANNING-phase ImplementationRule that pushes RequestedOrdering constraints through a SelectExpression that represents an IN-like pattern — a SELECT over one or more ExplodeExpressions (UNNEST of IN-lists) plus exactly one non-explode inner quantifier.

The rule validates the IN-like shape (explode count + 1 == total quantifiers, inner is correlated to all explodes, result is a QuantifiedObjectValue referencing the inner alias) and pushes the requested orderings verbatim to the inner quantifier's child Reference. This applies to both in-joins and in-unions.

Ports Java's PushRequestedOrderingThroughInLikeSelectRule.

func NewPushRequestedOrderingThroughInLikeSelectRule

func NewPushRequestedOrderingThroughInLikeSelectRule() *PushRequestedOrderingThroughInLikeSelectRule

func (PushRequestedOrderingThroughInLikeSelectRule) IsPreOrder

func (PushRequestedOrderingThroughInLikeSelectRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughInLikeSelectRule) Matcher

func (*PushRequestedOrderingThroughInLikeSelectRule) OnMatch

type PushRequestedOrderingThroughInsertRule

type PushRequestedOrderingThroughInsertRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughInsertRule is a PLANNING-phase ImplementationRule that propagates a RequestedOrdering constraint through an InsertExpression. Insert passes through rows unchanged (it writes them to the store and emits them for downstream counting/projection), so the requested ordering passes through unchanged to the child Reference.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op -- ImplementInsertRule handles the actual insert implementation.

Ports Java's PushRequestedOrderingThroughInsertRule.

func NewPushRequestedOrderingThroughInsertRule

func NewPushRequestedOrderingThroughInsertRule() *PushRequestedOrderingThroughInsertRule

func (PushRequestedOrderingThroughInsertRule) IsPreOrder

func (PushRequestedOrderingThroughInsertRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughInsertRule) Matcher

func (*PushRequestedOrderingThroughInsertRule) OnMatch

type PushRequestedOrderingThroughProjectionRule

type PushRequestedOrderingThroughProjectionRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughProjectionRule is a PLANNING-phase ImplementationRule that translates a RequestedOrdering constraint through a LogicalProjectionExpression's value mapping and pushes the translated ordering to the child Reference.

Projection is NOT ordering-transparent — sort keys reference post-projection column aliases which must be translated to their pre-projection source expressions via PushDownThroughValue. If all keys translate, the translated ordering is pushed to the child; if any key fails to translate, nothing is pushed.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op.

Ports Java's PushRequestedOrderingThroughSelectRule (for the projection case).

func NewPushRequestedOrderingThroughProjectionRule

func NewPushRequestedOrderingThroughProjectionRule() *PushRequestedOrderingThroughProjectionRule

func (PushRequestedOrderingThroughProjectionRule) IsPreOrder

func (PushRequestedOrderingThroughProjectionRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughProjectionRule) Matcher

func (*PushRequestedOrderingThroughProjectionRule) OnMatch

type PushRequestedOrderingThroughRecursiveUnionRule

type PushRequestedOrderingThroughRecursiveUnionRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughRecursiveUnionRule is a PLANNING-phase ImplementationRule that pushes a RequestedOrdering constraint through a RecursiveUnionExpression. Currently, it only allows pushing preserve-type orderings to both the initial and recursive legs.

Ports Java's PushRequestedOrderingThroughRecursiveUnionRule.

func NewPushRequestedOrderingThroughRecursiveUnionRule

func NewPushRequestedOrderingThroughRecursiveUnionRule() *PushRequestedOrderingThroughRecursiveUnionRule

func (PushRequestedOrderingThroughRecursiveUnionRule) IsPreOrder

func (PushRequestedOrderingThroughRecursiveUnionRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughRecursiveUnionRule) Matcher

func (*PushRequestedOrderingThroughRecursiveUnionRule) OnMatch

type PushRequestedOrderingThroughSelectExistentialRule

type PushRequestedOrderingThroughSelectExistentialRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughSelectExistentialRule is a PLANNING-phase ImplementationRule that pushes a preserve RequestedOrdering to the child of an existential quantifier within a SelectExpression.

Existential quantifiers (EXISTS subqueries) don't need specific orderings — they only check for the existence of at least one row. This rule unconditionally pushes preserve ordering to their child Reference so downstream rules know the ordering is unconstrained.

Ports Java's PushRequestedOrderingThroughSelectExistentialRule.

func NewPushRequestedOrderingThroughSelectExistentialRule

func NewPushRequestedOrderingThroughSelectExistentialRule() *PushRequestedOrderingThroughSelectExistentialRule

func (PushRequestedOrderingThroughSelectExistentialRule) IsPreOrder

func (PushRequestedOrderingThroughSelectExistentialRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughSelectExistentialRule) Matcher

func (*PushRequestedOrderingThroughSelectExistentialRule) OnMatch

type PushRequestedOrderingThroughSelectRule

type PushRequestedOrderingThroughSelectRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughSelectRule is a PLANNING-phase ImplementationRule that pushes RequestedOrdering constraints through a SelectExpression to every ForEach quantifier. Ordering parts are translated through the SELECT's result value (projection) so each child receives only the parts that can be expressed in that child's value space.

For preserve orderings, preserve is pushed through unchanged. For concrete orderings, PushDownThroughValue translates each ordering part through the projection.

Ports Java's PushRequestedOrderingThroughSelectRule.

func NewPushRequestedOrderingThroughSelectRule

func NewPushRequestedOrderingThroughSelectRule() *PushRequestedOrderingThroughSelectRule

func (PushRequestedOrderingThroughSelectRule) IsPreOrder

func (PushRequestedOrderingThroughSelectRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughSelectRule) Matcher

func (*PushRequestedOrderingThroughSelectRule) OnMatch

type PushRequestedOrderingThroughSortRule

type PushRequestedOrderingThroughSortRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughSortRule is a PLANNING-phase ImplementationRule that reads a LogicalSortExpression's sort keys and pushes them as a RequestedOrdering constraint to the child Reference. The sort expression is the SOURCE of ordering constraints — this rule creates the initial constraint that transparent rules (Distinct, Unique, Delete) then propagate further.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op — ImplementSortRule handles the actual sort elimination / implementation.

Ports Java's PushRequestedOrderingThroughSortRule.

func NewPushRequestedOrderingThroughSortRule

func NewPushRequestedOrderingThroughSortRule() *PushRequestedOrderingThroughSortRule

func (PushRequestedOrderingThroughSortRule) IsPreOrder

func (PushRequestedOrderingThroughSortRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughSortRule) Matcher

func (*PushRequestedOrderingThroughSortRule) OnMatch

type PushRequestedOrderingThroughTempTableInsertRule

type PushRequestedOrderingThroughTempTableInsertRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughTempTableInsertRule is a PLANNING-phase ImplementationRule that propagates a RequestedOrdering constraint through a TempTableInsertExpression. TempTableInsert passes through rows into a temp table -- sorting below produces the same final order as sorting above -- so the requested ordering passes through unchanged to the child Reference.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op -- ImplementTempTableInsertRule handles the actual insert implementation.

Ports Java's PushRequestedOrderingThroughInsertTempTableRule.

func NewPushRequestedOrderingThroughTempTableInsertRule

func NewPushRequestedOrderingThroughTempTableInsertRule() *PushRequestedOrderingThroughTempTableInsertRule

func (PushRequestedOrderingThroughTempTableInsertRule) IsPreOrder

func (PushRequestedOrderingThroughTempTableInsertRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughTempTableInsertRule) Matcher

func (*PushRequestedOrderingThroughTempTableInsertRule) OnMatch

type PushRequestedOrderingThroughUnionRule

type PushRequestedOrderingThroughUnionRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughUnionRule is a PLANNING-phase ImplementationRule that pushes a RequestedOrdering constraint through a LogicalUnionExpression to all of its child branch References.

For a union to produce ordered output, all branches must be independently ordered — a merge-union above combines the sorted streams. The first branch receives exhaustive orderings (all satisfying plans are needed to enumerate merge-compatible options); remaining branches receive the orderings as-is (they'll be specifically requested by the union implementation rule).

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op.

Ports Java's PushRequestedOrderingThroughUnionRule.

func NewPushRequestedOrderingThroughUnionRule

func NewPushRequestedOrderingThroughUnionRule() *PushRequestedOrderingThroughUnionRule

func (PushRequestedOrderingThroughUnionRule) IsPreOrder

func (PushRequestedOrderingThroughUnionRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughUnionRule) Matcher

func (*PushRequestedOrderingThroughUnionRule) OnMatch

type PushRequestedOrderingThroughUniqueRule

type PushRequestedOrderingThroughUniqueRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughUniqueRule is a PLANNING-phase ImplementationRule that propagates a RequestedOrdering constraint through a LogicalUniqueExpression. Unique (PK deduplication) preserves row order, so the requested ordering passes through unchanged to the child Reference.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op — ImplementUniqueRule handles the actual unique implementation.

Ports Java's PushRequestedOrderingThroughUniqueRule.

func NewPushRequestedOrderingThroughUniqueRule

func NewPushRequestedOrderingThroughUniqueRule() *PushRequestedOrderingThroughUniqueRule

func (PushRequestedOrderingThroughUniqueRule) IsPreOrder

func (PushRequestedOrderingThroughUniqueRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughUniqueRule) Matcher

func (*PushRequestedOrderingThroughUniqueRule) OnMatch

type PushRequestedOrderingThroughUpdateRule

type PushRequestedOrderingThroughUpdateRule struct {
	// contains filtered or unexported fields
}

PushRequestedOrderingThroughUpdateRule is a PLANNING-phase ImplementationRule that propagates a RequestedOrdering constraint through an UpdateExpression. Update passes through rows unchanged (it applies transforms and emits them for downstream counting/projection), so the requested ordering passes through unchanged to the child Reference.

Like Java, concrete orderings are translated through the OLD/NEW computation row. Only OLD fields can become input-provided ordering keys; NEW is bound after mutation and therefore reduces to a preserve request at this boundary.

This rule fires during the top-down constraint-propagation pass (constraintOnly=true). During the bottom-up implementation pass (constraintOnly=false) it is a no-op -- ImplementUpdateRule handles the actual update implementation.

Ports Java's PushRequestedOrderingThroughUpdateRule.

func NewPushRequestedOrderingThroughUpdateRule

func NewPushRequestedOrderingThroughUpdateRule() *PushRequestedOrderingThroughUpdateRule

func (PushRequestedOrderingThroughUpdateRule) IsPreOrder

func (PushRequestedOrderingThroughUpdateRule) IsPreOrder() bool

func (*PushRequestedOrderingThroughUpdateRule) Matcher

func (*PushRequestedOrderingThroughUpdateRule) OnMatch

type PushTypeFilterBelowFilterRule

type PushTypeFilterBelowFilterRule struct {
	// contains filtered or unexported fields
}

PushTypeFilterBelowFilterRule pushes a LogicalTypeFilter under a LogicalFilter — the inverse of PushFilterThroughTypeFilterRule.

TypeFilter([T], Filter(P, X))  →  Filter(P, TypeFilter([T], X))

Soundness: filter and type-filter commute under row admittance. Either order yields "rows of type-set T that satisfy P".

Optimization argument: TypeFilter is cheap (record-type dispatch). Pushing it CLOSER to the leaf means Filter operates on fewer rows (only T-typed rows reach the Filter). Wins when Filter's predicate is expensive.

Why we keep BOTH this AND PushFilterThroughTypeFilterRule: the two shapes coexist in the memo as alternatives. Cost-model extraction picks the cheaper one. Without cost, both shapes stay; exploration terminates because Reference.Insert's SemanticEquals fallback absorbs structurally-equivalent re-yields after the first round of rule firing.

Java equivalent: PushTypeFilterBelowFilterRule.

func NewPushTypeFilterBelowFilterRule

func NewPushTypeFilterBelowFilterRule() *PushTypeFilterBelowFilterRule

NewPushTypeFilterBelowFilterRule constructs the rule.

func (*PushTypeFilterBelowFilterRule) Matcher

Matcher returns the pattern.

func (*PushTypeFilterBelowFilterRule) OnMatch

OnMatch fires when the inner Quantifier ranges over a LogicalFilterExpression.

type PushUnionThroughFetchRule

type PushUnionThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushUnionThroughFetchRule handles Go's extra concat Union case. It has no direct Java counterpart: Java's ordered RecordQueryUnionOnValuesPlan maps to PushMergeSortUnionThroughFetchRule below.

func NewPushUnionThroughFetchRule

func NewPushUnionThroughFetchRule() *PushUnionThroughFetchRule

func (*PushUnionThroughFetchRule) Matcher

func (*PushUnionThroughFetchRule) OnMatch

type PushUnorderedUnionThroughFetchRule

type PushUnorderedUnionThroughFetchRule struct {
	// contains filtered or unexported fields
}

PushUnorderedUnionThroughFetchRule handles the UnorderedUnion case. Java: PushSetOperationThroughFetchRule<RecordQueryUnorderedUnionPlan>.

func NewPushUnorderedUnionThroughFetchRule

func NewPushUnorderedUnionThroughFetchRule() *PushUnorderedUnionThroughFetchRule

func (*PushUnorderedUnionThroughFetchRule) Matcher

func (*PushUnorderedUnionThroughFetchRule) OnMatch

type QueryPlanConstraint

type QueryPlanConstraint struct {
	// contains filtered or unexported fields
}

QueryPlanConstraint captures assumptions under which a plan match is valid. Wraps a QueryPredicate that must evaluate to true for the match to be applicable.

Ports Java's com.apple.foundationdb.record.query.plan.QueryPlanConstraint.

func NewQueryPlanConstraint

func NewQueryPlanConstraint(pred predicates.QueryPredicate) *QueryPlanConstraint

NewQueryPlanConstraint creates a constraint from a predicate.

func Tautology

func Tautology() *QueryPlanConstraint

Tautology returns a constraint that is always satisfied.

func (*QueryPlanConstraint) GetPredicate

func (c *QueryPlanConstraint) GetPredicate() predicates.QueryPredicate

GetPredicate returns the underlying predicate.

func (*QueryPlanConstraint) IsConstrained

func (c *QueryPlanConstraint) IsConstrained() bool

IsConstrained reports whether this constraint is non-trivial.

func (*QueryPlanConstraint) IsTautology

func (c *QueryPlanConstraint) IsTautology() bool

IsTautology reports whether this constraint is always satisfied.

type QueryPredicateSimplificationRule

type QueryPredicateSimplificationRule struct {
	// contains filtered or unexported fields
}

QueryPredicateSimplificationRule applies predicate simplification to a SelectExpression. Runs the predicate simplifier (predicates.SimplifyPredicateValues) on each predicate in the SelectExpression's predicate list and yields a new SelectExpression if any predicate changed.

Go's simplifier engine (SimplifyPredicateValues) walks the predicate tree and constant-folds Value operands — e.g. `name = 1+2` becomes `name = 3`. This is the Go equivalent of Java's ConstantFoldingRuleSet applied through Simplification.optimize.

Convergence: if no predicates changed (pointer-identity check), the rule does not yield. Constant-folding is idempotent, so repeated application converges in one step.

Ports Java's QueryPredicateSimplificationRule (ExplorationCascadesRule, 128 LOC).

func NewQueryPredicateSimplificationRule

func NewQueryPredicateSimplificationRule() *QueryPredicateSimplificationRule

func (*QueryPredicateSimplificationRule) Matcher

func (*QueryPredicateSimplificationRule) OnMatch

type ReachabilityCollector

type ReachabilityCollector struct {
	// contains filtered or unexported fields
}

ReachabilityCollector accumulates the plan-reachability tally for the planning runs a caller deliberately points at it. Attach one to a Planner with SetReachabilityCollector; a Planner with none collects nothing.

SCOPED TO AN INSTANCE, DELIBERATELY. This tally used to live in package variables behind a package mutex, and that shape produced exactly the class of failure this file exists to prevent — a number that reads as a measurement while being an artifact. The corpus ratchet reported edges=53748 / no-quantifier=96, precisely 3x the true 17916/32, because sibling tests in the same package plan the SAME corpus under t.Parallel and their planning accumulated into the tally. The mutex was no defence: it serialised the writes perfectly and still summed three tests into one answer. Locking shared state does not make it unshared.

The symptom there was an INFLATED count, which at least fails loudly against a ratchet. The dangerous direction is the other one: a concurrent Reset clearing a tally mid-flight yields a SILENT PASS — zero defects because nothing was counted. An instance nobody else holds cannot be reset by anyone else, so the question does not arise.

Collection is opt-in rather than always-on because the invariant is not yet clean: RFC-183 inherited a population of violations and is driving it to zero, so failing hard here today would simply disable the planner. Once the count reaches zero this graduates into verifyNoShell's company as an unconditional error — that is the whole point of measuring.

Collection happens at YIELD time, which is the only place the memo and the plan are both in hand. Groups legitimately hold alternatives at that moment, which is exactly why the check accepts ANY matching member: an earlier RFC-183 measurement counted a plan built on one of several alternatives as a divergence and had to be retracted (§13). Do not "tighten" this to compare against a single member — that reintroduces the retracted error, and narrowing a group to its used member is a real regression (it destroyed the InUnion alternative when tried on the IN-join rule).

The mutex remains because ONE collector may still legitimately be shared: the corpus harness threads a single collector through a whole baseline walk, and nothing promises that walk stays single-goroutine. It guards this instance's fields only, and makes no claim about anyone else's.

func NewReachabilityCollector

func NewReachabilityCollector() *ReachabilityCollector

NewReachabilityCollector returns a collector ready to be attached to a Planner. The zero value is equally usable; this exists so call sites read as "I am creating an instrument", not "I am declaring a variable".

func (*ReachabilityCollector) ComparedEdges

func (c *ReachabilityCollector) ComparedEdges() int

ComparedEdges returns the number of edges actually compared against a group — the anti-blindness signal. See collectReachability.

func (*ReachabilityCollector) Count

func (c *ReachabilityCollector) Count() int

Count returns the count of GENUINE defects: a plan child its quantifier's group cannot produce (ReasonAbsent), plus a quantifier ranging over an empty reference (ReasonEmptyGroup, a mis-seeded reference).

BOTH are defects, so both are counted. An earlier revision counted only ReasonAbsent, which made the headline "0" a zero of ONE reason code: an EmptyGroup regression would have slipped through the ratchet silently while the number still read zero. EmptyGroup happens to be 0 across the corpus today, so that filtering did not inflate any published figure — but a ratchet that cannot see a whole class of the defect it guards is exactly the "green while broken" failure this file exists to prevent.

ReasonNoQuantifier is the ONLY exclusion, and it is a real one rather than convenience: those are leaf adapters that model no quantifier for a plan child BY DESIGN. Folding them in would restate RFC-183 §12's over-count in a new form — a big number whose bulk is architecture working as intended. They are reported separately by ReachabilityReport and tracked as a modelling gap, not silently dropped.

func (*ReachabilityCollector) Edges

func (c *ReachabilityCollector) Edges() int

Edges returns every plan child walked, compared or not — the denominator for the proportional anti-blindness check.

func (*ReachabilityCollector) NoQuantifierCount

func (c *ReachabilityCollector) NoQuantifierCount() int

NoQuantifierCount returns edges whose plan child has NO quantifier at all.

Excluded from ReachabilityCount because it is dominated by leaf adapters that model no quantifier by design — but MEASURED AND RATCHETED separately, not waved through. The two-leg intersection that executed with zero memo edges (AggregateDataAccessRule passing nil quantifiers) was exactly a ReasonNoQuantifier bug, so a ratchet blind to this class cannot catch that defect recurring. "Explained by a category" is not "checked".

func (*ReachabilityCollector) Report

func (c *ReachabilityCollector) Report(maxSamples int) string

Report renders the accumulated tally: per-plan-type counts by reason, plus samples. Safe on a nil collector (reports zero) so a diagnostic path never has to branch on whether collection was on.

func (*ReachabilityCollector) Reset

func (c *ReachabilityCollector) Reset()

Reset clears the tally so one caller can measure several runs separately.

Kept because a long-lived collector is a legitimate shape, NOT because anything needs to undo another caller's accumulation — that was the old global's job and the reason it could silently zero a live measurement.

type ReachabilityViolation

type ReachabilityViolation struct {
	ParentType  string
	ChildIndex  int
	NumChildren int

	// Reason distinguishes a genuine unreachable edge from the two shapes
	// that merely LOOK like one. Counting without this conflates them, which
	// is how RFC-183 §12 over-counted 343 edges down to a true 158.
	Reason string

	ParentExplain string
	ChildExplain  string
	GroupExplain  string

	// Divergence names the first node where the executed child and the
	// nearest group member stop being equal. Explain alone cannot answer
	// this: some violations render identically on both sides.
	Divergence string
}

ReachabilityViolation is one plan child that its quantifier's group cannot produce.

func CheckPlanReachability

func CheckPlanReachability(expr expressions.RelationalExpression) []ReachabilityViolation

CheckPlanReachability reports every plan child that its quantifier's group cannot produce, for ONE expression: its own plan children against its own quantifiers, depth 1.

It deliberately does NOT recurse into group members. Every expression is checked when IT is yielded, so recursing re-counts the same edge once per ancestor — an earlier revision did exactly that and reported 3541 where the true figure is two orders of magnitude smaller.

A nil or non-physical expression yields no violations: this asks whether a plan is consistent with its memo, and there is nothing to ask of a non-plan.

func (ReachabilityViolation) String

func (v ReachabilityViolation) String() string

type ReadableIndexes

type ReadableIndexes struct {
	// contains filtered or unexported fields
}

ReadableIndexes is the planner's view of index state. It models Java's `Optional<Set<String>>` — absent means "every index is readable", present means "only these are" — plus a THIRD state Java does not need, because Java's filter is reached by every planning path and Go's is not.

Java's `Optional.empty()` is an affirmative assertion derived from `RecordStoreState`: *I consulted the store and every index is readable*. A Go planning run that never consulted a store has a different fact to state: *nobody asked*. Those are opposite epistemic states, and collapsing them onto one value is how an index whose state nobody checked comes to license a uniqueness proof (RFC-210 §5.1.1 — the collapse produced duplicate rows in review).

So the three states, and what each licenses:

unknown      (zero value, IndexStatesUnknown) — may back a scan, may NOT prove
all-readable (AllIndexesReadable)             — may back a scan, may prove
restricted   (OnlyReadableIndexes)            — the named ones may do both

The zero value stays PERMISSIVE for scanning on purpose. Planning happens from unit tests, offline harnesses and the DDL front end as well as from a live session, and only a live session can know index states; defaulting those other callers to "nothing is readable" would silently delete every index plan they have. It is demoted only as EVIDENCE, never as PERMISSION — which is what makes every unwritten planning path safe by construction rather than safe by everyone remembering to thread a config field.

An empty restricted allow-list ("nothing is readable", a store mid-rebuild) and an absent one are opposite instructions that a nil map would render identically, which is why the state is an explicit field rather than `names == nil`.

func AllIndexesReadable

func AllIndexesReadable() ReadableIndexes

AllIndexesReadable is Java's `Optional.empty()` — no restriction, and an AFFIRMATIVE claim that the store was consulted and every index came back strictly READABLE. Only a caller that actually read index state may mint it; manufacturing it from the absence of information re-creates the collapse the third state exists to prevent.

func IndexStatesUnknown

func IndexStatesUnknown() ReadableIndexes

IndexStatesUnknown is the zero value, stated explicitly so a caller can say what it means: this planning run never consulted a record store, so its indexes may be scanned but may not prove anything about their own state.

func OnlyReadableIndexes

func OnlyReadableIndexes(names map[string]struct{}) ReadableIndexes

OnlyReadableIndexes restricts planning to the named indexes, Java's `Optional.of(set)`. A nil or empty set is a real restriction meaning "no index may be scanned", not an absent one.

func (ReadableIndexes) Allows

func (r ReadableIndexes) Allows(indexName string) bool

Allows reports whether an index of this name may back a match candidate. UNKNOWN allows everything: the zero value is permissive for scanning.

func (ReadableIndexes) IndexStatesEstablished

func (r ReadableIndexes) IndexStatesEstablished() bool

IndexStatesEstablished reports whether this planning run affirmatively established index state — i.e. whether an index it admits may be trusted to carry the state the store reported, and so may back a metadata-property proof (RFC-210 §5.1 clause 1). False for the zero value.

Note this is NOT the negation of IsRestricted: a healthy store yields the UNRESTRICTED form (readableIndexesFrom returns AllIndexesReadable when every index is readable), so gating a proof on IsRestricted would enable it only while some unrelated index was mid-build. Exactly backwards.

func (ReadableIndexes) IsRestricted

func (r ReadableIndexes) IsRestricted() bool

IsRestricted reports whether an allow-list is present at all.

func (ReadableIndexes) SortedNames

func (r ReadableIndexes) SortedNames() []string

SortedNames returns the allow-listed names in a deterministic order, or nil when unrestricted. Used to key the plan cache: two planning runs that differ in which indexes were readable must not share a cached plan.

type ReferencedFields

type ReferencedFields struct {
	// contains filtered or unexported fields
}

ReferencedFields tracks which FieldValues are referenced by upstream operators (predicates, projections, ordering). Pushed top-down via PushReferencedFieldsThrough* rules so downstream operators can prune columns not needed by the query.

Ports Java's ReferencedFieldsConstraint.ReferencedFields.

func CombineReferencedFields

func CombineReferencedFields(current, added *ReferencedFields) (*ReferencedFields, bool)

CombineReferencedFields is the ReferencedFields lattice combine (Java ReferencedFieldsConstraint.combine): the UNION of both sets, with changed=false when the union adds nothing over current (Java's empty Optional — the push is subsumed). nil operands act as empty sets.

func EmptyReferencedFields

func EmptyReferencedFields() *ReferencedFields

EmptyReferencedFields returns an empty field set.

func FieldValuesFromValue

func FieldValuesFromValue(v values.Value) *ReferencedFields

FieldValuesFromValue extracts FieldValue names from a Value tree.

func NewReferencedFields

func NewReferencedFields(fields map[string]struct{}) *ReferencedFields

NewReferencedFields creates a ReferencedFields from a set of field names.

func (*ReferencedFields) IsEmpty

func (r *ReferencedFields) IsEmpty() bool

IsEmpty reports whether no fields are referenced.

func (*ReferencedFields) Union

Union returns a new ReferencedFields containing all fields from both this and other.

type RegularMatchInfo

type RegularMatchInfo struct {
	// contains filtered or unexported fields
}

RegularMatchInfo is the primary implementation of MatchInfo, representing a direct match between two expressions.

Ports Java's MatchInfo.RegularMatchInfo.

func NewRegularMatchInfo

func NewRegularMatchInfo(
	parameterBindingMap map[values.CorrelationIdentifier]*predicates.ComparisonRange,
	bindingAliasMap *AliasMap,
	predicateMap *PredicateMultiMap,
	matchedOrderingParts []*MatchedOrderingPart,
	maxMatchMap *MaxMatchMap,
	groupByMappings *GroupByMappings,
	rollUpToGroupingValues []values.Value,
	additionalPlanConstraint *QueryPlanConstraint,
) *RegularMatchInfo

NewRegularMatchInfo constructs a RegularMatchInfo. All collection fields are defensively copied.

func (*RegularMatchInfo) GetAdditionalPlanConstraint

func (r *RegularMatchInfo) GetAdditionalPlanConstraint() *QueryPlanConstraint

GetAdditionalPlanConstraint returns the additional plan constraint.

func (*RegularMatchInfo) GetBindingAliasMap

func (r *RegularMatchInfo) GetBindingAliasMap() *AliasMap

GetBindingAliasMap returns the alias map used for binding.

func (*RegularMatchInfo) GetChildPartialMatchMap

func (r *RegularMatchInfo) GetChildPartialMatchMap() map[values.CorrelationIdentifier]PartialMatch

GetChildPartialMatchMap returns a defensive copy of the selected child branch keyed by query-quantifier alias.

func (*RegularMatchInfo) GetChildPartialMatchMaybe

func (r *RegularMatchInfo) GetChildPartialMatchMaybe(alias values.CorrelationIdentifier) PartialMatch

IsAdjusted returns false -- RegularMatchInfo is not adjusted. GetChildPartialMatchMaybe returns the child PartialMatch for the given quantifier alias, or nil if no child match exists for that alias. Ports Java's RegularMatchInfo.getChildPartialMatchMaybe.

func (*RegularMatchInfo) GetConstraint

func (r *RegularMatchInfo) GetConstraint() *QueryPlanConstraint

GetConstraint composes the current expression's constraint with predicate and child-match constraints. Mirrors Java RegularMatchInfo.getConstraint().

func (*RegularMatchInfo) GetGroupByMappings

func (r *RegularMatchInfo) GetGroupByMappings() *GroupByMappings

GetGroupByMappings returns the group-by mappings.

func (*RegularMatchInfo) GetMatchedOrderingParts

func (r *RegularMatchInfo) GetMatchedOrderingParts() []*MatchedOrderingPart

GetMatchedOrderingParts returns the matched ordering parts.

func (*RegularMatchInfo) GetMaxMatchMap

func (r *RegularMatchInfo) GetMaxMatchMap() *MaxMatchMap

GetMaxMatchMap returns the maximum match map.

func (*RegularMatchInfo) GetParameterBindingMap

func (r *RegularMatchInfo) GetParameterBindingMap() map[values.CorrelationIdentifier]*predicates.ComparisonRange

GetParameterBindingMap returns the parameter binding map.

func (*RegularMatchInfo) GetPredicateMap

func (r *RegularMatchInfo) GetPredicateMap() *PredicateMultiMap

GetPredicateMap returns the predicate multi-map.

func (*RegularMatchInfo) GetRegularMatchInfo

func (r *RegularMatchInfo) GetRegularMatchInfo() *RegularMatchInfo

GetRegularMatchInfo returns itself.

func (*RegularMatchInfo) GetRollUpToGroupingValues

func (r *RegularMatchInfo) GetRollUpToGroupingValues() []values.Value

GetRollUpToGroupingValues returns the roll-up-to-grouping values, or nil if not applicable.

func (*RegularMatchInfo) IsAdjusted

func (r *RegularMatchInfo) IsAdjusted() bool

func (*RegularMatchInfo) IsRegular

func (r *RegularMatchInfo) IsRegular() bool

IsRegular returns true -- RegularMatchInfo is a regular match.

func (*RegularMatchInfo) RequiresPrimaryKeyDistinct

func (r *RegularMatchInfo) RequiresPrimaryKeyDistinct() bool

RequiresPrimaryKeyDistinct reports whether this expression's semantic mapping can multiply one logical record and therefore requires a primary-key distinct repair after its local filtering is applied.

func (*RegularMatchInfo) SetChildPartialMatch

func (r *RegularMatchInfo) SetChildPartialMatch(alias values.CorrelationIdentifier, pm PartialMatch)

SetChildPartialMatch sets the child PartialMatch for a quantifier alias. Called by MatchIntermediateRule when building composite matches.

type RegularTranslationMap

type RegularTranslationMap struct {
	// contains filtered or unexported fields
}

RegularTranslationMap is the standard implementation of TranslationMap backed by a map from source aliases to translation functions.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.values.translation.RegularTranslationMap.

func ComposeTranslationMaps

func ComposeTranslationMaps(maps ...*RegularTranslationMap) *RegularTranslationMap

ComposeTranslationMaps creates a new RegularTranslationMap by composing multiple translation maps. Each source alias must be unique across all maps; panics on conflict.

Ports Java's RegularTranslationMap.compose.

func EmptyTranslationMap

func EmptyTranslationMap() *RegularTranslationMap

EmptyTranslationMap returns a shared empty translation map. Ports Java's TranslationMap.empty() / RegularTranslationMap.empty().

func RebaseWithAliasMap

func RebaseWithAliasMap(am *AliasMap) *RegularTranslationMap

RebaseWithAliasMap creates a RegularTranslationMap from an AliasMap where each source-to-target mapping generates a translation function that calls RebaseLeaf on the leaf value with the target alias.

Ports Java's TranslationMap.rebaseWithAliasMap / RegularTranslationMap.rebaseWithAliasMap.

func TranslationMapOfAliases

func TranslationMapOfAliases(source, target values.CorrelationIdentifier) *RegularTranslationMap

TranslationMapOfAliases creates a RegularTranslationMap that translates a single source alias to a target alias by rebasing leaf values. Equivalent to RebaseWithAliasMap(AliasMapOfAliases(source, target)).

Ports Java's TranslationMap.ofAliases / RegularTranslationMap.ofAliases.

func (*RegularTranslationMap) ApplyTranslationFunction

func (m *RegularTranslationMap) ApplyTranslationFunction(sourceAlias values.CorrelationIdentifier, leafValue values.LeafValue) values.Value

ApplyTranslationFunction applies the translation function for the given source alias to the leaf value. Panics if no translation function exists for sourceAlias.

func (*RegularTranslationMap) ContainsSourceAlias

func (m *RegularTranslationMap) ContainsSourceAlias(sourceAlias values.CorrelationIdentifier) bool

ContainsSourceAlias reports whether this map has a translation for the given source alias.

func (*RegularTranslationMap) DefinesOnlyIdentities

func (m *RegularTranslationMap) DefinesOnlyIdentities() bool

DefinesOnlyIdentities reports whether all mappings are identity mappings. An empty function map with an identity (or absent) alias map is considered identity-only.

Ports Java's RegularTranslationMap.definesOnlyIdentities.

func (*RegularTranslationMap) GetAliasMap

func (m *RegularTranslationMap) GetAliasMap() (*AliasMap, bool)

GetAliasMap returns the underlying AliasMap if this translation map was created from one. Returns (nil, false) for plain translation maps without an alias map.

func (*RegularTranslationMap) GetTargetAlias

GetTargetAlias returns the target alias for the given source alias by consulting the underlying AliasMap. Returns (zero, false) if there is no AliasMap or the source is not mapped.

type RemoveProjectionRule

type RemoveProjectionRule struct {
	// contains filtered or unexported fields
}

RemoveProjectionRule removes a LogicalProjectionExpression when the inner is already a physical plan (not a fetch). The projection is unnecessary because the inner plan already produces the correct row shape. The rule simply yields the inner plan, dropping the projection wrapper.

Mirrors Java's RemoveProjectionRule.

func NewRemoveProjectionRule

func NewRemoveProjectionRule() *RemoveProjectionRule

func (*RemoveProjectionRule) Matcher

func (*RemoveProjectionRule) OnMatch

func (r *RemoveProjectionRule) OnMatch(call *ImplementationRuleCall)

type RemoveRangeOneRule

type RemoveRangeOneRule struct {
	// contains filtered or unexported fields
}

RemoveRangeOneRule ports Java's RemoveRangeOneRule (ExplorationCascadesRule <SelectExpression>). It drops a ForEach quantifier over a RANGE(0, 1) table-function value from a SelectExpression when (1) the Select has MORE THAN ONE quantifier (removing the sole quantifier would change cardinality) and (2) that quantifier's alias is UNREFERENCED by the result value, the predicates, and every sibling quantifier. A RANGE(0, 1) is a single-row cross-join identity inserted to keep a Select non-empty (see DecorrelateValuesRule); once other real quantifiers exist and nothing reads it, it is dead weight.

This is distinct from the Go-invented LIMIT-removal rule that wore this name and was deleted in RFC-188 — this is Java's ACTUAL RemoveRangeOneRule. It is latent today (Go only mints the RANGE(0, 1) placeholder when ALL quantifiers are values-boxes, yielding a single-quantifier Select that the >1 guard excludes), ported for parity.

func NewRemoveRangeOneRule

func NewRemoveRangeOneRule() *RemoveRangeOneRule

NewRemoveRangeOneRule constructs the rule.

func (*RemoveRangeOneRule) Matcher

Matcher returns the pattern.

func (*RemoveRangeOneRule) OnMatch

func (r *RemoveRangeOneRule) OnMatch(call *ExpressionRuleCall)

OnMatch removes an unreferenced RANGE(0, 1) quantifier.

type ResultCompensationFunction

type ResultCompensationFunction struct {
	// contains filtered or unexported fields
}

ResultCompensationFunction handles final result shape transformation. Ports Java's PredicateMultiMap.ResultCompensationFunction.

func NewImpossibleResultCompensation

func NewImpossibleResultCompensation() *ResultCompensationFunction

NewImpossibleResultCompensation creates a ResultCompensationFunction that is both needed and impossible.

func NewResultCompensationFunction

func NewResultCompensationFunction(needed bool) *ResultCompensationFunction

NewResultCompensationFunction creates a ResultCompensationFunction.

func NoResultCompensation

func NoResultCompensation() *ResultCompensationFunction

NoResultCompensation returns a ResultCompensationFunction that indicates no result compensation is needed. Mirrors Java's ResultCompensationFunction.noCompensationNeeded().

func ResultCompensationOfValue

func ResultCompensationOfValue(v values.Value) *ResultCompensationFunction

ResultCompensationOfValue creates a ResultCompensationFunction from a result Value. When applied, it translates the value through the translation map. Ports Java's ResultCompensationFunction.ofValue.

func (*ResultCompensationFunction) Amend

func (f *ResultCompensationFunction) Amend(
	unmatchedAggregateMap *BiMap[values.CorrelationIdentifier, values.Value],
	amendedMatchedAggregateMap map[values.Value]values.Value,
) *ResultCompensationFunction

Amend recreates the result compensation function with updated aggregate value mappings. Ports Java's ResultCompensationFunction.amend.

func (*ResultCompensationFunction) ApplyCompensationForResult

func (f *ResultCompensationFunction) ApplyCompensationForResult(tm TranslationMap) values.Value

ApplyCompensationForResult applies this compensation by translating the result value through the translation map. Returns the compensated result value. Ports Java's ResultCompensationFunction.applyCompensationForResult.

func (*ResultCompensationFunction) IsImpossible

func (f *ResultCompensationFunction) IsImpossible() bool

IsImpossible reports whether result compensation is impossible.

func (*ResultCompensationFunction) IsNeeded

func (f *ResultCompensationFunction) IsNeeded() bool

IsNeeded reports whether result compensation must be applied.

type RewriteOuterJoinRule

type RewriteOuterJoinRule struct {
	// contains filtered or unexported fields
}

RewriteOuterJoinRule canonicalizes a LEFT OUTER join SelectExpression into the nested form Java uses (rules/RewriteOuterJoinRule.java + expressions/OuterJoin Expression.java): the ON-predicates are pushed BELOW the null-extension boundary into a correlated null-supplying inner SELECT, and the join becomes an INNER SelectExpression over the preserved leg plus a NULL-on-empty quantifier carrying the outer-join semantics.

Go encodes an outer join as a flat 2-quantifier SelectExpression with joinType=JoinLeftOuter and the ON-predicates in the top-level predicate list (the translator keeps WHERE *above* the join for LEFT OUTER, so every predicate here is an ON-predicate). Java's RewriteOuterJoinRule matches an OuterJoinExpression; Go matches the LEFT-OUTER SelectExpression directly, since Go carries outer-join on the flag rather than a dedicated logical box. That is the only deviation from a 1:1 port, forced by Go's representation.

Why this is needed (RFC-150 Phase-2b Piece 2): without it, neither PartitionBinarySelectRule nor PushFilterBelowJoinRule fires (both guard on JoinInner), so no leg becomes correlated, and the data-access FlatMap path (yieldGeneralFlatMap) never produces a correlated LEFT-OUTER FlatMap. This rule creates the correlation Java's single path relies on — it is what let the former Go-only tryFlatMapPlan (a hand-rolled inner-pushed-residual shortcut) be retired: the correlated LEFT-OUTER FlatMap now emerges from the standard data-access path.

Correctness (the LEFT-OUTER axis): the ON-predicates MUST filter the inner stream BEFORE the empty→NULL extension. Folding them into innerSelect (below the NULL-on-empty quantifier) does exactly that — applying an ON-predicate above the FlatMap, after the NULL row is injected, would drop that row and silently degrade LEFT OUTER into INNER. The outer SelectExpression carries NO predicates, so there is nothing left to apply above the join. Mirrors Java RewriteOuterJoinRule.

func NewRewriteOuterJoinRule

func NewRewriteOuterJoinRule() *RewriteOuterJoinRule

NewRewriteOuterJoinRule constructs the rule.

func (*RewriteOuterJoinRule) Matcher

func (*RewriteOuterJoinRule) OnMatch

func (r *RewriteOuterJoinRule) OnMatch(call *ExpressionRuleCall)

OnMatch rewrites a LEFT OUTER SelectExpression into the INNER + null-on-empty form.

type RuleCall

type RuleCall struct {
	// Bindings is the PlannerBindings the rule's matcher built up
	// during BindMatches. Rule bodies use Get[T] / Get to retrieve
	// matched values.
	Bindings *matching.PlannerBindings
	// contains filtered or unexported fields
}

RuleCall is the context a CascadesRule receives on every match. OnMatch reads bindings and appends replacement expressions via Yield.

func (*RuleCall) Err

func (c *RuleCall) Err() error

Err returns the first error reported by the rule body.

func (*RuleCall) Fail

func (c *RuleCall) Fail(err error)

Fail records the first rule-body error. Later failures cannot hide the original cause, and yields staged before the failure are discarded.

func (*RuleCall) Yield

func (c *RuleCall) Yield(expr any)

Yield records a replacement expression. The planner reads the accumulated list after OnMatch returns.

func (*RuleCall) Yielded

func (c *RuleCall) Yielded() []any

Yielded returns the replacements this RuleCall has accumulated. The returned slice is defensive. Failed calls expose no yields.

type ScanDirection

type ScanDirection int

ScanDirection indicates whether an access scans forward, reverse, or both. Ports Java's AbstractDataAccessRule.ScanDirection.

const (
	ScanDirectionForward ScanDirection = iota
	ScanDirectionReverse
	ScanDirectionBoth
)

func SatisfiesAnyRequestedOrderings

func SatisfiesAnyRequestedOrderings(
	pm PartialMatch,
	requestedOrderings []*properties.RequestedOrdering,
) ([]*properties.RequestedOrdering, *ScanDirection)

SatisfiesAnyRequestedOrderings filters requestedOrderings to those satisfied by the partial match. Returns the satisfied orderings and the scan direction, or nil if none are satisfied.

Ports Java's AbstractDataAccessRule.satisfiesAnyRequestedOrderings.

func SatisfiesRequestedOrdering

func SatisfiesRequestedOrdering(pm PartialMatch, ro *properties.RequestedOrdering) *ScanDirection

SatisfiesRequestedOrdering checks if a PartialMatch's matched ordering parts satisfy a RequestedOrdering. Returns the scan direction needed, or nil if the ordering is not satisfied.

Ports Java's AbstractDataAccessRule.satisfiesRequestedOrdering.

type ScanWithFetchMatchCandidate

type ScanWithFetchMatchCandidate interface {
	MatchCandidate

	// PushValueThroughFetch attempts to translate a value from the
	// full-record domain (correlated to sourceAlias) to the
	// index-entry domain (correlated to targetAlias). Returns the
	// translated value and true on success; nil and false if the
	// value cannot be expressed using only the index entry's columns.
	//
	// Ports Java's ScanWithFetchMatchCandidate.pushValueThroughFetch.
	PushValueThroughFetch(
		value values.Value,
		sourceAlias values.CorrelationIdentifier,
		targetAlias values.CorrelationIdentifier,
	) (values.Value, bool)
}

ScanWithFetchMatchCandidate is the interface for match candidates that support the covering-index-to-fetch pattern. When an index scan covers enough columns, the fetch can be deferred; when it doesn't, a FetchFromPartialRecordPlan wraps the scan.

The key method is PushValueThroughFetch, which attempts to translate a single Value from the full-record domain (correlated to sourceAlias) to the index-entry domain (correlated to targetAlias). If translation succeeds, the value can be evaluated on the partial/covering record without a full fetch.

Ports Java's `com.apple.foundationdb.record.query.plan.cascades.ScanWithFetchMatchCandidate`.

type SealedGraphExpansion

type SealedGraphExpansion struct {
	// contains filtered or unexported fields
}

SealedGraphExpansion is an immutable, deduplicated version of GraphExpansion. Created by Seal(). Used to (repeatedly) build SelectExpressions. Mirrors Java's `GraphExpansion.Sealed`.

func (*SealedGraphExpansion) BuildSelect

BuildSelect creates a SelectExpression using the sealed columns' RecordConstructorValue as the result value. Mirrors Java's `Sealed.buildSelect()`.

func (*SealedGraphExpansion) BuildSelectWithResultValue

func (s *SealedGraphExpansion) BuildSelectWithResultValue(resultValue values.Value) (*expressions.SelectExpression, error)

BuildSelectWithResultValue creates a SelectExpression with an externally-provided result value. The sealed expansion must have no result columns. A violation is returned rather than panicked so rule drivers can discard every staged effect from the failed construction. Mirrors Java's `Sealed.buildSelectWithResultValue(resultValue)`.

func (*SealedGraphExpansion) GetPlaceholders

func (s *SealedGraphExpansion) GetPlaceholders() []*predicates.Placeholder

GetPlaceholders returns the sealed placeholder list.

func (*SealedGraphExpansion) GetPredicates

func (s *SealedGraphExpansion) GetPredicates() []predicates.QueryPredicate

GetPredicates returns the sealed predicate list.

func (*SealedGraphExpansion) GetQuantifiers

func (s *SealedGraphExpansion) GetQuantifiers() []expressions.Quantifier

GetQuantifiers returns the sealed quantifier list.

func (*SealedGraphExpansion) GetResultColumns

func (s *SealedGraphExpansion) GetResultColumns() []GraphExpansionColumn

GetResultColumns returns the sealed result columns.

func (*SealedGraphExpansion) GetResultValue

func (s *SealedGraphExpansion) GetResultValue() values.Value

GetResultValue returns the RecordConstructorValue built from the result columns during sealing. Nil if there were no columns.

type SelectMergeRule

type SelectMergeRule struct {
	// contains filtered or unexported fields
}

SelectMergeRule merges nested Select/Filter expressions into a single, flatter SelectExpression. When a SelectExpression has a ForEach quantifier whose child Reference holds a LogicalFilterExpression or another SelectExpression, this rule pulls up the child's quantifiers and predicates into the parent.

Pattern (LogicalFilter child):

SelectExpression(
  ForEach(alias=A) → Ref[LogicalFilter(preds, ForEach(alias=B) → Ref[scan])],
  ...other quantifiers...,
  outerPreds
)

Rewrite:

SelectExpression(
  ForEach(alias=B) → Ref[scan],
  ...other quantifiers...,
  rebase(outerPreds, A→B) + preds
)

Pattern (SelectExpression child):

SelectExpression(
  ForEach(alias=A) → Ref[SelectExpression([q1,q2,...], childPreds, childResult)],
  ...other quantifiers...,
  outerPreds
)

Rewrite:

SelectExpression(
  q1, q2, ...,
  ...other quantifiers...,
  rebase(outerPreds, A→childResult) + rebase(childPreds) + outerPreds
)

Ports Java's SelectMergeRule (ImplementationCascadesRule). Placed in EXPLORE as an ExpressionRule because Go's PLANNING phase does not support multi-round implementation. Functionally equivalent: the merged Select is explored + matched + implemented normally.

Convergence: each firing strictly reduces nesting depth. A flat Select with no mergeable children causes zero yields.

func NewSelectMergeRule

func NewSelectMergeRule() *SelectMergeRule

func (*SelectMergeRule) Matcher

func (*SelectMergeRule) OnMatch

func (r *SelectMergeRule) OnMatch(call *ExpressionRuleCall)

type SingleMatchedAccess

type SingleMatchedAccess struct {
	// contains filtered or unexported fields
}

SingleMatchedAccess wraps a PartialMatch with its computed Compensation and metadata about how it can be used for data access. It is a value object produced during the prepareMatchesAndCompensations phase of AbstractDataAccessRule.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.rules.AbstractDataAccessRule.SingleMatchedAccess.

func NewSingleMatchedAccess

func NewSingleMatchedAccess(
	partialMatch PartialMatch,
	compensation Compensation,
	candidateTopAlias values.CorrelationIdentifier,
	reverseScanOrder bool,
	topToTopTranslationMap TranslationMap,
	satisfyingRequestedOrderings []*properties.RequestedOrdering,
) *SingleMatchedAccess

NewSingleMatchedAccess constructs a SingleMatchedAccess. The satisfyingRequestedOrderings slice is defensively copied.

Mirrors Java's SingleMatchedAccess constructor.

func PrepareMatchesAndCompensations

func PrepareMatchesAndCompensations(
	partialMatches []PartialMatch,
	requestedOrderings []*properties.RequestedOrdering,
	_ PlanContext,
) []*SingleMatchedAccess

PrepareMatchesAndCompensations compensates and sorts partial matches by coverage (descending bound predicate count). For each PartialMatch:

  • assigns a unique candidateTopAlias
  • computes compensation via CompensateCompleteMatch
  • derives the query-top to candidate-top translation from the MaxMatchMap
  • translates requested orderings into the candidate's top scope
  • computes satisfying orderings via SatisfiesAnyRequestedOrderings
  • creates a forward- or reverse-scan SingleMatchedAccess

Returns the accesses sorted by coverage (highest first).

Ports Java's AbstractDataAccessRule.prepareMatchesAndCompensations.

func (*SingleMatchedAccess) GetCandidateTopAlias

func (s *SingleMatchedAccess) GetCandidateTopAlias() values.CorrelationIdentifier

GetCandidateTopAlias returns the correlation identifier for the candidate's top-level quantifier.

func (*SingleMatchedAccess) GetCompensation

func (s *SingleMatchedAccess) GetCompensation() Compensation

GetCompensation returns the compensation computed for this match.

func (*SingleMatchedAccess) GetPartialMatch

func (s *SingleMatchedAccess) GetPartialMatch() PartialMatch

GetPartialMatch returns the partial match this access was derived from.

func (*SingleMatchedAccess) GetPulledUpGroupByMappingsForOrdering

func (s *SingleMatchedAccess) GetPulledUpGroupByMappingsForOrdering() *GroupByMappings

GetPulledUpGroupByMappingsForOrdering returns the pulled-up group-by mappings, lazily computed on first access. Mirrors Java's getPulledUpGroupByMappingsForOrdering().

func (*SingleMatchedAccess) GetSatisfyingRequestedOrderings

func (s *SingleMatchedAccess) GetSatisfyingRequestedOrderings() []*properties.RequestedOrdering

GetSatisfyingRequestedOrderings returns the set of requested orderings that this access satisfies.

func (*SingleMatchedAccess) GetTopToTopTranslationMap

func (s *SingleMatchedAccess) GetTopToTopTranslationMap() TranslationMap

GetTopToTopTranslationMap returns the translation map between query and candidate top-level aliases.

func (*SingleMatchedAccess) IsReverseScanOrder

func (s *SingleMatchedAccess) IsReverseScanOrder() bool

IsReverseScanOrder reports whether the scan should be in reverse order.

func (*SingleMatchedAccess) String

func (s *SingleMatchedAccess) String() string

String returns a human-readable representation mirroring Java's SingleMatchedAccess.toString(). Note: Java's label for reverseScanOrder is inverted (true -> "forward", false -> "reverse"); we match that exactly.

type SinkLimitIntoVectorScanRule

type SinkLimitIntoVectorScanRule struct {
	// contains filtered or unexported fields
}

SinkLimitIntoVectorScanRule folds a Limit(k) that sits DIRECTLY above a distance-ordered VectorIndexScan (RFC-156 Phase B) back into the scan's self-limiting top-k mode — restoring the legacy one-shot search(k) fast path for the no-residual and partition-only cases, byte-for-byte.

Pattern:

Limit(k) → VectorIndexScan(ordered)   →   VectorIndexScan(self-limit k)

The rule fires ONLY when the Limit is directly above the ordered vector scan with NO intervening row-dropping / order-disturbing operator (i.e. no residual Filter). When a residual Filter intervenes the rule does NOT fire, and the scan must stream its re-ranked horizon so the Filter+Limit collect the true k nearest MATCHING rows — the whole point of the Phase B fix.

This is the decoupled "sink" half of the Cascades match-then-implement split (Graefe 1995): the match candidate emits ONE canonical ordered-stream form (it never sinks k and never introspects residuals); this rule, and only this rule, folds k into the scan when it is provably safe to do so.

Modeled on MergeFetchIntoCoveringIndexRule (a physical→physical ImplementationRule that yields the inner leaf in place of its wrapper when a structural condition holds).

func NewSinkLimitIntoVectorScanRule

func NewSinkLimitIntoVectorScanRule() *SinkLimitIntoVectorScanRule

func (*SinkLimitIntoVectorScanRule) Matcher

func (*SinkLimitIntoVectorScanRule) OnMatch

type SortConstantKeysElimRule

type SortConstantKeysElimRule struct {
	// contains filtered or unexported fields
}

SortConstantKeysElimRule eliminates a LogicalSort whose every key is a row-independent constant.

Sort([ConstantValue(42), ConstantValue('x')], X)  →  X

SQL semantic: ORDER BY 42 sorts rows by a value that's the same for every row — the resulting order is undefined / arbitrary. Same for ORDER BY 'x', BY TRUE, BY NULL. Stripping the sort doesn't change result-set semantics; it just removes wasted work.

Soundness: IsConstantValue answers "this Value's Evaluate is independent of the row context" — exactly the property we need for "all rows would sort to the same key". When every sort key has IsConstantValue=true, the sort produces no ordering refinement.

Termination: yields the inner expression directly (no new wrapper). Pointer-identity dedup on second fire — the inner is the same expression object as before.

Edge case — empty sort: Sort([]) is the Unsorted form, which UnsortedSortElim handles independently. Letting both rules fire is harmless (UnsortedSortElim hits first; this rule's emptiness check declines).

func NewSortConstantKeysElimRule

func NewSortConstantKeysElimRule() *SortConstantKeysElimRule

NewSortConstantKeysElimRule constructs the rule.

func (*SortConstantKeysElimRule) Matcher

Matcher returns the pattern.

func (*SortConstantKeysElimRule) OnMatch

func (r *SortConstantKeysElimRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the sort has at least one key AND every key's Value is row-context-independent (IsConstantValue=true).

type SortDedupKeysRule

type SortDedupKeysRule struct {
	// contains filtered or unexported fields
}

SortDedupKeysRule removes structurally-duplicate sort keys from a LogicalSort.

Sort([k1, k2, k1, k3], X)  →  Sort([k1, k2, k3], X)

SQL semantics: ORDER BY a, b, a is equivalent to ORDER BY a, b — the second `a` adds no ordering refinement (rows already in a-order remain in a-order regardless of subsequent keys repeating).

Sort keys are compared by Explain text (the same textual bridge as LogicalSortExpression.EqualsWithoutChildren), so "duplicate key" is decided structurally. Reverse flag matters: a key with the same Value but different Reverse is NOT a duplicate (different ordering direction).

Termination: yields Sort with the deduped key list, REUSING the inner Quantifier. Pointer-identity dedup absorbs second fire.

func NewSortDedupKeysRule

func NewSortDedupKeysRule() *SortDedupKeysRule

NewSortDedupKeysRule constructs the rule.

func (*SortDedupKeysRule) Matcher

Matcher returns the pattern.

func (*SortDedupKeysRule) OnMatch

func (r *SortDedupKeysRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the sort key list contains at least one duplicate-by-Value-and-Reverse pair.

type SortElisionSelector

type SortElisionSelector interface {
	// OrderedChildWinner returns a physical member of childRef whose
	// ordering satisfies sortExpr's keys, or nil (sort must stay).
	OrderedChildWinner(sortExpr *expressions.LogicalSortExpression, childRef *expressions.Reference) expressions.RelationalExpression
	// OrderingSourceRef reports whether expr merely PRESERVES its
	// input's order, returning the child group the ordering flows from.
	// Extraction pins that group when eliding a sort (see
	// rebuildOrderedSpine).
	OrderingSourceRef(expr expressions.RelationalExpression) (*expressions.Reference, bool)
}

SortElisionSelector is the optional extension of BestMemberSelector a selector implements to enable extraction-time sort elimination.

type SortMergeRule

type SortMergeRule struct {
	// contains filtered or unexported fields
}

SortMergeRule eliminates a wasted inner sort when its result is immediately re-sorted by an outer LogicalSortExpression.

Sort([k1, k2, ...]) over Sort([j1, j2, ...]) over X
→
Sort([k1, k2, ...]) over X

The outer sort fully determines the final order — the inner sort's work is discarded the moment the outer re-orders. Eliminating the inner sort never changes the result rows OR their final order; it only saves the wasted intermediate ordering work.

Edge cases:

  • If the outer sort is unsorted (Sort([])), the rewrite would destroy the inner's ordering. Decline in that case — the UnsortedSortElim rule handles unsorted Sorts on its own pass by eliminating them from outer-side inputs.
  • If the inner sort is unsorted (Sort([])), the rule still fires: dropping a no-op intermediate is cheap and structurally cleaner. (Technically UnsortedSortElim would also have caught the inner by itself — but having both rules cooperate is fine.)

Java equivalent: emerges from cost preference for fewer operators. Seed implements directly so the optimiser's logical phase produces a concretely-cleaner tree before B4 cost lands.

func NewSortMergeRule

func NewSortMergeRule() *SortMergeRule

NewSortMergeRule constructs the rule.

func (*SortMergeRule) Matcher

func (r *SortMergeRule) Matcher() matching.BindingMatcher

Matcher returns the pattern.

func (*SortMergeRule) OnMatch

func (r *SortMergeRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the inner Quantifier ranges over another LogicalSort AND the outer is non-empty (otherwise the rewrite would destroy the inner's ordering — see doc comment).

type SplitSelectExtractIndependentQuantifiersRule

type SplitSelectExtractIndependentQuantifiersRule struct {
	// contains filtered or unexported fields
}

SplitSelectExtractIndependentQuantifiersRule splits a SelectExpression into two when one or more ForEach quantifiers ranging over ExplodeExpressions are fully independent — not correlated to any other quantifier in the SelectExpression.

The independent quantifiers are extracted into an outer SelectExpression; the correlated quantifiers remain in the inner. This is safe and often beneficial because the extracted quantifiers range over ExplodeExpressions (bounded cardinality), so the outer cross-product is small. Star-join optimisations benefit from this partitioning.

Ports Java's SplitSelectExtractIndependentQuantifiersRule (184 LOC).

Algorithm:

  1. Match a SelectExpression with at least one ForEach quantifier ranging over an ExplodeExpression.
  2. Build a PartiallyOrderedSet of all quantifier aliases, with dependency edges derived from correlation analysis (quantifier A depends on alias B if A's inner expression is correlated to B).
  3. Compute the eligible set — aliases with no dependencies (in-degree zero in the partial order). Only explode-backed ForEach quantifiers that are eligible are candidates for extraction.
  4. Partition quantifiers into lower (non-eligible or non-explode) and upper (eligible explode). Both partitions must be non-empty, and the lower must contain at least one ForEach.
  5. Guard: skip if the SelectExpression is "simple" — no predicates and the result value doesn't reference any explode alias.
  6. Yield: lower quantifiers + all predicates go into the inner SelectExpression; the outer gets the upper quantifiers plus a new ForEach over the inner, with the inner's flowed-object value as its result.

Convergence: each firing strictly reduces the number of quantifiers in the inner SelectExpression. The isSimpleSelect guard prevents infinite expansion when the result is already trivially structured.

func NewSplitSelectExtractIndependentQuantifiersRule

func NewSplitSelectExtractIndependentQuantifiersRule() *SplitSelectExtractIndependentQuantifiersRule

func (*SplitSelectExtractIndependentQuantifiersRule) Matcher

func (*SplitSelectExtractIndependentQuantifiersRule) OnMatch

type StreamingAggFromIndexRule

type StreamingAggFromIndexRule struct {
	// contains filtered or unexported fields
}

StreamingAggFromIndexRule directly converts a GroupByExpression into a streaming aggregation over an ordered index scan when an index's leading columns cover the grouping keys. This fires even without an explicit Sort expression in the tree — the index ordering is sufficient.

GroupBy(keys=[k1, k2], aggs=[...], FullScan)
  → StreamingAgg(IndexScan(full-range, index on (k1, k2, ...)))

Without this rule, the planner would need Sort(keys, Scan) below the GroupBy for the streaming agg path to trigger. This rule closes the gap for queries like "SELECT region, COUNT(*) FROM t GROUP BY region" where the user doesn't specify ORDER BY but an index on (region) exists.

func NewStreamingAggFromIndexRule

func NewStreamingAggFromIndexRule() *StreamingAggFromIndexRule

func (*StreamingAggFromIndexRule) Matcher

func (*StreamingAggFromIndexRule) OnMatch

type Task

type Task interface {
	Run(ctx context.Context, p *Planner)
}

Task is the task-stack driver's unit of work. Tasks receive the run-scoped context and may push more tasks. Implementations must stop at their bounded cancellation seams; the driver also checks before and after every task.

type TempTable

type TempTable struct {
	// contains filtered or unexported fields
}

TempTable is a mutable in-memory buffer used as intermediate storage for recursive CTE evaluation. Thread-safe.

func NewTempTable

func NewTempTable() *TempTable

func (*TempTable) Add

func (t *TempTable) Add(element any)

func (*TempTable) Clear

func (t *TempTable) Clear()

func (*TempTable) IsEmpty

func (t *TempTable) IsEmpty() bool

func (*TempTable) Len

func (t *TempTable) Len() int

func (*TempTable) List

func (t *TempTable) List() []any

type TieBrokenCostSelector

type TieBrokenCostSelector interface {
	TieBrokenCostLess(stats properties.StatisticsProvider) func(a, b expressions.RelationalExpression) bool
}

TieBrokenCostSelector is the optional extension of BestMemberSelector a selector implements to supply a TOTAL-ORDER cost comparator for the extraction fallbacks. The scalar CostLessWith is not total (Cost ties are routine), and GetBest resolves ties by member insertion order — which shifts across plannings. The planner supplies its hash-tie-broken comparator through this seam; without it the fallbacks keep the raw comparator (test-only paths).

type TransformExprTask

type TransformExprTask struct {
	Phase PlannerPhase
	Ref   *expressions.Reference
	Expr  expressions.RelationalExpression
	Rule  ExpressionRule
}

TransformExprTask fires a single ExpressionRule on a (group, expression) pair. Yields go to exploratory members (ref.Insert). Mirrors Java's TransformExpression for ExplorationCascadesRule.

func (*TransformExprTask) Run

func (t *TransformExprTask) Run(ctx context.Context, p *Planner)

type TransformImplTask

type TransformImplTask struct {
	Phase PlannerPhase
	Ref   *expressions.Reference
	Expr  expressions.RelationalExpression
	Rule  ImplementationRule
}

TransformImplTask fires a single ImplementationRule on a (group, expression) pair. Yields go to final members (ref.InsertFinal). Mirrors Java's TransformExpression for ImplementationCascadesRule.

func (*TransformImplTask) Run

func (t *TransformImplTask) Run(ctx context.Context, p *Planner)

type TranslationFunction

type TranslationFunction func(sourceAlias values.CorrelationIdentifier, leafValue values.LeafValue) values.Value

TranslationFunction is the function type used to specify the translation to take place when a leaf value with a particular source alias is encountered.

Ports Java's TranslationMap.TranslationFunction functional interface.

type TranslationMap

type TranslationMap interface {
	// ContainsSourceAlias reports whether this map has a translation
	// for the given source alias.
	ContainsSourceAlias(sourceAlias values.CorrelationIdentifier) bool

	// GetTargetAlias returns the target alias for the given source
	// alias. Returns (target, true) if the mapping exists, or
	// (zero, false) if not.
	GetTargetAlias(sourceAlias values.CorrelationIdentifier) (values.CorrelationIdentifier, bool)

	// ApplyTranslationFunction applies the translation function for
	// the given source alias to the leaf value. Panics if no
	// translation exists for sourceAlias (callers must check
	// ContainsSourceAlias first).
	//
	// Ports Java's TranslationMap.applyTranslationFunction.
	ApplyTranslationFunction(sourceAlias values.CorrelationIdentifier, leafValue values.LeafValue) values.Value

	// GetAliasMap returns the underlying AliasMap if this translation
	// map is alias-based. Returns (aliasMap, true) when available, or
	// (nil, false) for translation maps that are not backed by an
	// AliasMap.
	GetAliasMap() (*AliasMap, bool)

	// DefinesOnlyIdentities reports whether all mappings in this
	// translation map are identity mappings (source == target with
	// identity translation function). An empty map is considered
	// identity-only.
	DefinesOnlyIdentities() bool
}

TranslationMap is a map-like interface used to specify translations of correlation references within a Value tree. When a plan rule rewrites an expression and correlation identifiers change, a TranslationMap tells the rewriter how to map each old (source) alias to a new (target) alias and what function to apply to leaf values referencing that alias.

Ports Java's com.apple.foundationdb.record.query.plan.cascades.values.translation.TranslationMap.

type TranslationMapBuilder

type TranslationMapBuilder struct {
	// contains filtered or unexported fields
}

TranslationMapBuilder constructs a RegularTranslationMap incrementally using a fluent When/Then API.

Ports Java's RegularTranslationMap.Builder.

func NewTranslationMapBuilder

func NewTranslationMapBuilder() *TranslationMapBuilder

NewTranslationMapBuilder creates an empty builder.

func (*TranslationMapBuilder) Build

Build creates an immutable RegularTranslationMap from the builder's current state. If the function map is empty, returns the shared empty translation map.

Ports Java's RegularTranslationMap.Builder.build.

func (*TranslationMapBuilder) Compose

Compose merges another RegularTranslationMap into this builder. Panics if any source alias in other already exists in the builder.

Ports Java's RegularTranslationMap.Builder.compose.

func (*TranslationMapBuilder) When

When starts a When/Then chain for the given source alias. Returns a TranslationMapWhen that must be completed with Then.

Ports Java's RegularTranslationMap.Builder.when.

func (*TranslationMapBuilder) WhenAny

WhenAny starts a WhenAny/Then chain for multiple source aliases. The translation function will be applied to all of them.

Ports Java's RegularTranslationMap.Builder.whenAny.

type TranslationMapFunc

type TranslationMapFunc func(realizedAlias values.CorrelationIdentifier) TranslationMap

TranslationMapFunc maps a realized base-quantifier alias to a TranslationMap that rebases compensated predicates/result values from the candidate's top alias to that realized alias. Ports Java's Function<CorrelationIdentifier, TranslationMap> passed to Compensation.apply / applyFinal / applyAllNeededCompensations (AbstractDataAccessRule: realizedAlias -> TranslationMap.ofAliases(candidateTopAlias, realizedAlias)).

type TranslationMapWhen

type TranslationMapWhen struct {
	// contains filtered or unexported fields
}

TranslationMapWhen is an intermediate builder step that captures the source alias for a When/Then chain.

Ports Java's RegularTranslationMap.Builder.When inner class.

func (*TranslationMapWhen) Then

Then completes the When/Then chain by specifying the translation function for the source alias.

type TranslationMapWhenAny

type TranslationMapWhenAny struct {
	// contains filtered or unexported fields
}

TranslationMapWhenAny is an intermediate builder step that captures multiple source aliases for a WhenAny/Then chain.

Ports Java's RegularTranslationMap.Builder.WhenAny inner class.

func (*TranslationMapWhenAny) Then

Then completes the WhenAny/Then chain by applying the same translation function to all source aliases.

type Traversal

type Traversal struct {
	// contains filtered or unexported fields
}

Traversal is a pre-computed walk of an expression DAG rooted at a Reference. It indexes expressions by their References for efficient lookup during the matching phase.

Ports the core surface of Java's `com.apple.foundationdb.record.query.plan.cascades.Traversal`. Java uses a Guava MutableNetwork<Reference, ReferencePath>; the Go version uses maps and slices for the same semantics without the external dependency. The traversal is immutable after construction — callers must not mutate the underlying expression DAG.

func ExpandValueIndex

func ExpandValueIndex(candidate MatchCandidate) *Traversal

ExpandValueIndex builds a Traversal from an index definition, producing a candidate expression tree with Placeholder predicates for each index column. The resulting Traversal is used by matching rules to match query predicates against index columns.

The output structure matches Java's ValueIndexExpansionVisitor:

MatchableSortExpression(sortParamIDs, isReverse=false,
  SelectExpression(resultValue,
    [ForEach(FullUnorderedScanExpression(recordTypes))],
    [Placeholder(param0, FieldValue("col0")),
     Placeholder(param1, FieldValue("col1")),
     ...]))

When the candidate exposes its record-layer key-expression AST, fan-out fields are expanded with the same nested Select/Explode topology as Java's KeyExpressionExpansionVisitor. Candidates without that optional structure, and supported non-fan-out indexes, retain the flat-column expansion.

func ExpandVectorIndex

func ExpandVectorIndex(c *VectorIndexScanMatchCandidate) *Traversal

ExpandVectorIndex builds the match-candidate Traversal for a VECTOR (HNSW) index. Unlike ExpandValueIndex (one FieldValue placeholder per column), the vector expansion splits the index columns at the partition/vector boundary:

  • each partition (key) column → an ordinary Placeholder(FieldValue(col)), which binds the equality prefix that selects the HNSW partition;
  • the single vector (value) column → one *distance placeholder* whose value is the metric-specific DistanceRowNumberValue(partitionFields, [vecField]). The query's QUALIFY ROW_NUMBER() OVER(... ORDER BY <distance>) predicate lowers (transformComparisonMaybe) to exactly that value, so it binds by structural match.

Ports Java's VectorIndexExpansionVisitor.expand / createDistanceValuePlaceholder.

func NewTraversal

func NewTraversal(rootRef *expressions.Reference) *Traversal

NewTraversal builds a Traversal by walking the expression DAG from rootRef using a recursive DFS, visiting each Reference at most once.

Mirrors Java's `Traversal.withRoot` + `collectNetwork`.

func (*Traversal) FindReferencingExpressions

func (t *Traversal) FindReferencingExpressions(
	childRefs []*expressions.Reference,
) map[*expressions.Reference][]expressions.RelationalExpression

FindReferencingExpressions returns a map from Reference to the set of expressions that reference any of the given child References through their quantifiers. Used by MatchIntermediateRule to walk upward from already-matched leaves.

For each childRef in the input, looks up its parents in the childToParents index and collects all parent (ref, expr) pairs. Deduplicates: if the same (ref, expr) pair appears for multiple childRefs, only includes it once.

func (*Traversal) GetLeafReferences

func (t *Traversal) GetLeafReferences() []*expressions.Reference

GetLeafReferences returns references that contain at least one leaf expression (an expression with zero quantifiers). Used by MatchLeafRule to find the bottom of the candidate's expression tree.

func (*Traversal) GetParentRefPairs

func (t *Traversal) GetParentRefPairs(childRef *expressions.Reference) []refExprPair

GetParentRefPairs returns all (parentRef, parentExpr) pairs that reference the given child Reference via a quantifier. Mirrors Java's Traversal.getParentRefPaths (outEdges in the network).

func (*Traversal) GetRootReference

func (t *Traversal) GetRootReference() *expressions.Reference

GetRootReference returns the root reference of the traversal.

type TypeFilterMergeRule

type TypeFilterMergeRule struct {
	// contains filtered or unexported fields
}

TypeFilterMergeRule consolidates two nested LogicalTypeFilter expressions into a single one with the INTERSECTION of their record-type sets.

TypeFilter([T_outer...]) over TypeFilter([T_inner...]) over X
→
TypeFilter([T_outer ∩ T_inner]) over X

SQL-equivalent: nested type narrowing is the same as one narrowing to the intersection. If the intersection is empty the rule still fires — downstream rules will fold the empty-type-filter into a no-row-emission no-op.

Java equivalent: the planner would naturally derive this via the type-narrowing rules. Go implements it directly as a static rewrite.

func NewTypeFilterMergeRule

func NewTypeFilterMergeRule() *TypeFilterMergeRule

NewTypeFilterMergeRule constructs the rule.

func (*TypeFilterMergeRule) Matcher

Matcher returns the pattern.

func (*TypeFilterMergeRule) OnMatch

func (r *TypeFilterMergeRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the inner is also a LogicalTypeFilter; yields a single TypeFilter with the intersection of the type sets.

type TypeFilterRedundantOverScanRule

type TypeFilterRedundantOverScanRule struct {
	// contains filtered or unexported fields
}

TypeFilterRedundantOverScanRule eliminates a LogicalTypeFilter whose record-type allow-set is a SUPERSET of (or equal to) the inner FullUnorderedScan's record-type set — the filter rejects nothing.

TypeFilter([A, B, C], Scan(A, B))   → Scan(A, B)
TypeFilter([A], Scan(A))            → Scan(A)
TypeFilter([B], Scan(A, B))         → no change (B is a strict subset)

Why this matters: convertScan emits a single-record-type Scan, and an upstream `TypeFilter` is sometimes layered on by callers that don't know the scan was already narrowed to that type. Without this rule the planner would carry the redundant operator to the physical phase for the cost model to discount; the rewrite is a pure simplification (no information lost).

Java equivalent: handled implicitly by the cost preference for fewer operators. Go implements it directly because the static rewrite is trivially safe and produces a concretely-simpler plan tree.

func NewTypeFilterRedundantOverScanRule

func NewTypeFilterRedundantOverScanRule() *TypeFilterRedundantOverScanRule

NewTypeFilterRedundantOverScanRule constructs the rule.

func (*TypeFilterRedundantOverScanRule) Matcher

Matcher returns the pattern.

func (*TypeFilterRedundantOverScanRule) OnMatch

OnMatch fires when the inner Quantifier ranges over a FullUnorderedScanExpression AND every type in the scan's set is allowed by the type-filter (i.e. scan ⊆ filter).

type UnionMergeRule

type UnionMergeRule struct {
	// contains filtered or unexported fields
}

UnionMergeRule flattens a LogicalUnion whose any child Quantifier ranges over another LogicalUnion. The flattened result has all inner-Union children promoted to siblings of the outer-Union's other children.

Union(A, Union(B, C), D)
→
Union(A, B, C, D)

SQL-equivalent: UNION ALL is associative, so chained nested UNION ALL collapses without semantic change. Java's planner would derive this via cost preference for fewer operator nodes; Go implements it directly.

Fires once per OnMatch — the first inner-Union child triggers a rewrite that promotes ALL inner-Union children at once. If multiple children are themselves Unions, repeated rule fires (driven by the planner's iteration loop) collapse them in turn.

func NewUnionMergeRule

func NewUnionMergeRule() *UnionMergeRule

NewUnionMergeRule constructs the rule.

func (*UnionMergeRule) Matcher

func (r *UnionMergeRule) Matcher() matching.BindingMatcher

Matcher returns the pattern.

func (*UnionMergeRule) OnMatch

func (r *UnionMergeRule) OnMatch(call *ExpressionRuleCall)

OnMatch examines each child Quantifier; if any ranges over a LogicalUnion, yields a flattened Union with that inner-Union's children promoted in place. Yields nothing if no child is a Union.

type UnionSingletonElimRule

type UnionSingletonElimRule struct {
	// contains filtered or unexported fields
}

UnionSingletonElimRule eliminates a LogicalUnion with exactly one child — UNION ALL of a single input is just that input.

Union([Q]) → inner of Q

func NewUnionSingletonElimRule

func NewUnionSingletonElimRule() *UnionSingletonElimRule

NewUnionSingletonElimRule constructs the rule.

func (*UnionSingletonElimRule) Matcher

Matcher returns the pattern.

func (*UnionSingletonElimRule) OnMatch

func (r *UnionSingletonElimRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the union has exactly one child.

type UnplannableIndexOnlyResidualError

type UnplannableIndexOnlyResidualError struct {
	// Predicate is the offending predicate's string form, for diagnostics.
	Predicate string
}

UnplannableIndexOnlyResidualError reports that the only physical plan the planner could produce evaluates an index-only predicate (e.g. a vector K-NN DistanceRank over a DistanceRowNumberValue) as a residual filter over a base scan. Such a predicate can ONLY be produced by an index scan that binds it; as a residual it is not evaluable (Comparison.EvalAgainst would panic). This means no index could serve the predicate — e.g. a `QUALIFY ... ORDER BY cosine_distance(...)` against an index declared with a different metric, or a distance query on a column with no vector index — so the query is not plannable, exactly as Java leaves it (its single Compensation path stamps the match impossible). The query must fail to plan rather than build a plan that panics at execution.

func (*UnplannableIndexOnlyResidualError) Error

type UnresolvedResultTypeFloors

type UnresolvedResultTypeFloors struct {
	MinReads int
	MinSites int
}

UnresolvedResultTypeFloors are the two populations this census refuses to let collapse silently.

There is deliberately NO floor on the unresolved count and no zero to defend: RFC-213 has not been implemented, so the unresolved reads are the DEFECT and their number is a measurement, not a contract. What is guarded is the reading.

  • MinReads guards VACUITY. With no classified reads at all, a later "unresolved is now 0" is indistinguishable from success, so every finding denominated against this census becomes unreadable.

  • MinSites guards a DEAD INSTRUMENT. MinReads alone cannot see it: one hot consumer clears any read floor by orders of magnitude while every other deciding site reports nothing, and the census then speaks for a population it stopped observing.

BOTH ARE COLLAPSE GUARDS — the alarm is downward, and that is a statement with a shelf life. If RFC-213 lands and a consumer stops reading result types because it no longer needs to, zero becomes that site's steady state and MinSites becomes unsatisfiable. Reconcile it with the new expected value then; do not relax it to whatever the run produced, and do not delete it, which would leave the retired consumer's return unwatched.

A nil *UnresolvedResultTypeFloors, and equally a floors value of all zeroes, is a NO-OP by construction — the shape a narrowed run needs, where a whole-corpus population claim has nothing it can honestly decide.

type UnsortedSortElimRule

type UnsortedSortElimRule struct {
	// contains filtered or unexported fields
}

UnsortedSortElimRule eliminates a LogicalSort whose sort-key list is empty (the no-op sort produced by UnsortedLogicalSortExpression or by query rewrites that drop all sort keys).

Pattern:

LogicalSort([]) over X
→
X

This is the sort analogue of NoOpFilterRule. A sort with no keys preserves order — equivalent to no sort at all.

Java equivalent: not a dedicated rule, but the planner's cost model would naturally prefer the un-wrapped X over the no-op-Sort wrapper. Go implements it directly.

func NewUnsortedSortElimRule

func NewUnsortedSortElimRule() *UnsortedSortElimRule

NewUnsortedSortElimRule constructs the rule.

func (*UnsortedSortElimRule) Matcher

Matcher returns the pattern.

func (*UnsortedSortElimRule) OnMatch

func (r *UnsortedSortElimRule) OnMatch(call *ExpressionRuleCall)

OnMatch fires when the sort is unsorted (empty key list).

type ValueEquivalence

type ValueEquivalence interface {
	// IsDefinedEqual reports whether two values are axiomatically equal
	// under this equivalence. Returns a ConstrainedBoolean that may
	// carry a QueryPlanConstraint (the equality holds only if the
	// constraint is satisfied at plan execution time).
	IsDefinedEqual(left, right values.Value) ConstrainedBoolean

	// IsDefinedEqualAlias reports whether two correlation identifiers
	// are axiomatically equal under this equivalence.
	IsDefinedEqualAlias(left, right values.CorrelationIdentifier) ConstrainedBoolean
}

ValueEquivalence defines axiomatic equality relationships between Values beyond structural equality. Structural equality (Value.semanticEquals) compares two values within the same scope. ValueEquivalence enables cross-scope comparisons by declaring that certain values are considered equal under a mapping (e.g., two QuantifiedObjectValues are equal if their aliases are mapped via an AliasMap).

Ports Java's com.apple.foundationdb.record.query.plan.cascades.ValueEquivalence.

func EmptyValueEquivalence

func EmptyValueEquivalence() ValueEquivalence

EmptyValueEquivalence returns a ValueEquivalence where no values are considered equal. Ports Java's ValueEquivalence.empty().

type ValueIndexLikeMatchCandidate

type ValueIndexLikeMatchCandidate interface {
	MatchCandidate

	// GetBaseType returns the base record type for this candidate.
	// Ports Java's WithBaseQuantifierMatchCandidate.getBaseType().
	GetBaseType() values.Type

	// GetColumnSize returns the number of key columns in the index
	// (or primary key). Ports Java's MatchCandidate.getColumnSize().
	GetColumnSize() int

	// CreatesDuplicates reports whether the index can produce
	// duplicate entries per record (e.g., a fan-out/repeated-field
	// index). Ports Java's MatchCandidate.createsDuplicates().
	CreatesDuplicates() bool

	// HasAndOrderedByRecordTypeKey reports whether the index key
	// starts with the record type key, partitioning the index by
	// record type. Ports Java's
	// MatchCandidate.hasAndOrderedByRecordTypeKey().
	HasAndOrderedByRecordTypeKey() bool

	// GetSargableAliasesRequiredForBinding returns the set of
	// sargable parameter aliases that MUST be bound for this
	// candidate to be valid. For example, if the index starts with
	// a record type key, the first alias is required.
	// Ports Java's MatchCandidate.getSargableAliasesRequiredForBinding().
	GetSargableAliasesRequiredForBinding() []values.CorrelationIdentifier
}

ValueIndexLikeMatchCandidate is the interface for match candidates defined over value-based index-like data structures — secondary value indexes (ValueIndexScanMatchCandidate) and the primary scan (PrimaryScanMatchCandidate).

Java's ValueIndexLikeMatchCandidate provides default implementations for computeMatchedOrderingParts and computeOrderingFromScanComparisons. In Go these are not embedded as defaults (Go has no default methods); they live in the OrderingPartsComputer interface and package-level functions respectively. This interface captures the structural contract — implementors carry a base type and column-level metadata beyond what the base MatchCandidate requires.

Ports Java's `com.apple.foundationdb.record.query.plan.cascades.ValueIndexLikeMatchCandidate`.

type ValueIndexScanMatchCandidate

type ValueIndexScanMatchCandidate struct {
	// contains filtered or unexported fields
}

ValueIndexScanMatchCandidate represents a secondary index as a match candidate. Each index key column has a corresponding sargable alias; predicate matching binds comparisons to these aliases to determine which prefix of the index key can be used for a scan.

Ports the consumed surface of Java's `ValueIndexScanMatchCandidate`: the candidate expression Traversal (built lazily), key-column names + sargable aliases, per-column function bridges (CARDINALITY), and duplicate-creation tracking. Java additionally materializes index-value Values and ordering alias lists on the candidate; Go derives ordering from the column list at match-adjustment time (matched_ordering_part.go) instead.

func NewValueIndexScanMatchCandidate

func NewValueIndexScanMatchCandidate(
	indexName string,
	recordTypes []string,
	columnNames []string,
	sargableAliases []values.CorrelationIdentifier,
	flowedType values.Type,
	unique bool,
	pkColumnNames []string,
) *ValueIndexScanMatchCandidate

NewValueIndexScanMatchCandidate constructs a match candidate for a secondary index. columnNames and sargableAliases must be parallel slices in index key column order (left-to-right): columnNames[i] is the field name for the i-th key column, sargableAliases[i] is the correlation identifier used for predicate binding. This compatibility constructor has no duplicate/cardinality metadata, so the candidate deliberately remains UNKNOWN and cannot plan until a root key expression with FAN_OUT structure is attached. Metadata-backed scalar callers should use NewValueIndexScanMatchCandidateWithFunctions with an explicit false createsDuplicatesSignal.

func NewValueIndexScanMatchCandidateWithFunctions

func NewValueIndexScanMatchCandidateWithFunctions(
	indexName string,
	recordTypes []string,
	columnNames []string,
	columnFunctions []string,
	sargableAliases []values.CorrelationIdentifier,
	flowedType values.Type,
	unique bool,
	pkColumnNames []string,
	createsDuplicatesSignal *bool,
) *ValueIndexScanMatchCandidate

NewValueIndexScanMatchCandidateWithFunctions is NewValueIndexScanMatchCandidate plus a parallel columnFunctions slice (see the struct field). Pass nil columnFunctions for an all-plain-field index. A non-empty columnFunctions[i] (FunctionKindCardinality) makes the i-th column's match Value CardinalityValue(FieldValue(col)) so a CARDINALITY() predicate/sort binds.

func (*ValueIndexScanMatchCandidate) CandidateName

func (c *ValueIndexScanMatchCandidate) CandidateName() string

CandidateName returns the index name.

func (*ValueIndexScanMatchCandidate) ColumnValue

func (c *ValueIndexScanMatchCandidate) ColumnValue(i int, base values.Value) values.Value

ColumnValue returns the match Value for the i-th index key column over the given base (the QuantifiedObjectValue of the index's record source). For a plain field this is FieldValue(base, col); for a CARDINALITY()-keyed column it is CardinalityValue(FieldValue(base, col)). This is the single source of truth the predicate-placeholder expansion AND the ordered-index-scan sort matching both consult, so a CARDINALITY() query value binds to the index by Value-tree equality (Java: the match candidate carries the column's Value).

func (*ValueIndexScanMatchCandidate) ComputeBoundParameterPrefixMap

ComputeBoundParameterPrefixMap walks the sargable aliases in order and collects the longest prefix that satisfies index scan discipline:

  • N equality-bound parameters (any number, including 0)
  • followed by at most ONE inequality-bound parameter
  • stops at the first unbound (empty) parameter or after the first inequality

Mirrors Java's default `MatchCandidate.computeBoundParameterPrefixMap`.

func (*ValueIndexScanMatchCandidate) ComputeMatchedOrderingParts

func (c *ValueIndexScanMatchCandidate) ComputeMatchedOrderingParts(
	matchInfo MatchInfo,
	sortParameterIDs []values.CorrelationIdentifier,
	isReverse bool,
) []*MatchedOrderingPart

ComputeMatchedOrderingParts computes ordering parts for each index column, using bound comparison ranges from the match info. Ports Java's ValueIndexLikeMatchCandidate.computeMatchedOrderingParts.

func (*ValueIndexScanMatchCandidate) CreatesDuplicates

func (c *ValueIndexScanMatchCandidate) CreatesDuplicates() bool

CreatesDuplicates reports whether the index may produce duplicate entries per record. A structured FAN_OUT root is authoritative even when an optional external signal is missing or incorrectly false. UNKNOWN is represented by the conservative true value because this bool-shaped compatibility contract has no abstain state; DistinctRecordsSignal retains the tri-state form. Implements ValueIndexLikeMatchCandidate.

func (*ValueIndexScanMatchCandidate) DistinctRecordsSignal

func (c *ValueIndexScanMatchCandidate) DistinctRecordsSignal() *bool

DistinctRecordsSignal returns the fan-out signal for the DistinctRecords property. A structured FAN_OUT root establishes true even when the optional IndexDef signal is missing or stale. Without structural evidence, a missing signal remains nil (unknown, so the property safely abstains).

func (*ValueIndexScanMatchCandidate) GetBaseType

func (c *ValueIndexScanMatchCandidate) GetBaseType() values.Type

GetBaseType returns the base record type for this candidate. Implements ValueIndexLikeMatchCandidate.

func (*ValueIndexScanMatchCandidate) GetColumnNames

func (c *ValueIndexScanMatchCandidate) GetColumnNames() []string

GetColumnNames returns the ordered column-name list (one per index key column, parallel to GetSargableAliases).

func (*ValueIndexScanMatchCandidate) GetColumnSize

func (c *ValueIndexScanMatchCandidate) GetColumnSize() int

GetColumnSize returns the number of key columns in the index. Implements ValueIndexLikeMatchCandidate.

func (*ValueIndexScanMatchCandidate) GetCommonPrimaryKeyValues

func (c *ValueIndexScanMatchCandidate) GetCommonPrimaryKeyValues() []values.Value

GetCommonPrimaryKeyValues returns the index's structural common primary key (RFC-189 B3), or nil when the def supplied none.

func (*ValueIndexScanMatchCandidate) GetKeyComponentTypes

func (c *ValueIndexScanMatchCandidate) GetKeyComponentTypes() []values.Type

GetKeyComponentTypes returns physical types aligned with index columns.

func (*ValueIndexScanMatchCandidate) GetPKColumnNames

func (c *ValueIndexScanMatchCandidate) GetPKColumnNames() []string

GetPKColumnNames returns the primary-key column names of the record type the index ranges over, in PK order. Consumers append the trimPrimaryKey'd remainder after the index key columns to model the FULL entry key (Java: ValueIndexExpansionVisitor.fullKey) — index entries are (index key, primary key), so the scan's sort order and its matched ordering parts extend into the PK suffix. The PK is NOT part of the sargable surface (see unmatchedFieldsForIndex in planning_cost_model.go): unlike Java, Go's candidate never folds the PK into sargableAliases, and that invariant must hold.

func (*ValueIndexScanMatchCandidate) GetPredicateProto

func (c *ValueIndexScanMatchCandidate) GetPredicateProto() *gen.Predicate

GetPredicateProto returns a defensive copy of the sparse-index predicate, or nil for a full index.

func (*ValueIndexScanMatchCandidate) GetPrimaryKeyComponentTypes

func (c *ValueIndexScanMatchCandidate) GetPrimaryKeyComponentTypes() []values.Type

GetPrimaryKeyComponentTypes returns types aligned with pkColumnNames.

func (*ValueIndexScanMatchCandidate) GetRecordTypes

func (c *ValueIndexScanMatchCandidate) GetRecordTypes() []string

GetRecordTypes returns which record types this index covers.

func (*ValueIndexScanMatchCandidate) GetRootKeyExpression

func (c *ValueIndexScanMatchCandidate) GetRootKeyExpression() *gen.KeyExpression

GetRootKeyExpression returns a defensive copy of the record-layer key-expression AST, or nil when the candidate was built from flat metadata.

func (*ValueIndexScanMatchCandidate) GetSargableAliases

func (c *ValueIndexScanMatchCandidate) GetSargableAliases() []values.CorrelationIdentifier

GetSargableAliases returns the ordered parameter list (one per index key column).

func (*ValueIndexScanMatchCandidate) GetSargableAliasesRequiredForBinding

func (c *ValueIndexScanMatchCandidate) GetSargableAliasesRequiredForBinding() []values.CorrelationIdentifier

GetSargableAliasesRequiredForBinding returns the set of sargable aliases that must be bound for the candidate to be valid. For standard value indexes, no aliases are required (the default). Implements ValueIndexLikeMatchCandidate.

func (*ValueIndexScanMatchCandidate) GetTraversal

func (c *ValueIndexScanMatchCandidate) GetTraversal() *Traversal

GetTraversal returns the Traversal of this candidate's expression tree, built lazily on first access via ExpandValueIndex. The traversal is stable once computed (sync.Once). Ports Java's ValueIndexScanMatchCandidate.getTraversal().

func (*ValueIndexScanMatchCandidate) GetValueColumnNames

func (c *ValueIndexScanMatchCandidate) GetValueColumnNames() []string

GetValueColumnNames returns the covering-only (FDB VALUE part) column names, or nil for a non-covering root.

func (*ValueIndexScanMatchCandidate) HasAndOrderedByRecordTypeKey

func (c *ValueIndexScanMatchCandidate) HasAndOrderedByRecordTypeKey() bool

HasAndOrderedByRecordTypeKey reports whether the index key starts with the record type key. For standard value indexes this is false; only indexes explicitly prefixed by recordType() return true. Implements ValueIndexLikeMatchCandidate.

func (*ValueIndexScanMatchCandidate) HasOpaqueFilter

func (c *ValueIndexScanMatchCandidate) HasOpaqueFilter() bool

HasOpaqueFilter reports whether this candidate filters records through a predicate the planner cannot inspect. Such an index is never COMPLETE: it cannot stand in for the base table, and its UNIQUE declaration constrains only the records its filter admitted.

func (*ValueIndexScanMatchCandidate) IsUnique

func (c *ValueIndexScanMatchCandidate) IsUnique() bool

IsUnique reports whether the index enforces uniqueness.

func (*ValueIndexScanMatchCandidate) PushValueThroughFetch

func (c *ValueIndexScanMatchCandidate) PushValueThroughFetch(
	value values.Value,
	sourceAlias values.CorrelationIdentifier,
	targetAlias values.CorrelationIdentifier,
) (values.Value, bool)

PushValueThroughFetch attempts to translate a value from the full-record domain to the index-entry domain. Returns the translated value and true on success; nil and false otherwise. Implements ScanWithFetchMatchCandidate.

func (*ValueIndexScanMatchCandidate) ToScanPlan

ToScanPlan converts the matched prefix into a physical plan. The plan is wrapped in a FetchFromPartialRecordPlan with a TranslateValueFunction that can translate FieldValues referencing covered index columns. This enables push-through rules (C-6) to push filters/maps below the fetch when they reference covered columns.

Matches Java's ScanWithFetchMatchCandidate architecture where every index scan is wrapped in a Fetch that carries the translation function.

func (*ValueIndexScanMatchCandidate) WithCommonPrimaryKey

WithCommonPrimaryKey sets the index's structural common primary key on the (freshly constructed) candidate and returns it (RFC-189 B3).

func (*ValueIndexScanMatchCandidate) WithKeyComponentTypes

func (c *ValueIndexScanMatchCandidate) WithKeyComponentTypes(types []values.Type) *ValueIndexScanMatchCandidate

WithKeyComponentTypes attaches authoritative physical index-key types to a freshly constructed candidate.

func (*ValueIndexScanMatchCandidate) WithOpaqueFilter

WithOpaqueFilter marks the candidate as filtering records through a predicate that has NO proto representation, so nothing can be attached to the candidate graph and no matcher can account for it.

It is deliberately a SEPARATE flag from predicateProto rather than a synthetic predicate. A synthesized stand-in would have to be some concrete proto, and every such proto is either provably tautological (which normalizes away, back to "complete") or claims a filter shape the index does not actually have, which the matcher would then try to compensate. The honest statement is "a filter exists and its content is unknowable", and that is a different fact from any predicate.

The only sound reading of an unknowable filter is the pessimistic one, so this flag is one-way: it can be set, never cleared, and unlike WithPredicateProto there is no tautology escape — a Go closure cannot be proved to reject nothing.

func (*ValueIndexScanMatchCandidate) WithPredicateProto

WithPredicateProto marks the candidate as a SPARSE (filtered) index by attaching the stored index predicate (RecordMetaDataProto.Index.predicate). The expansion converts it into a candidate-side QueryPredicate (ValueIndexExpansionVisitor.java:138-162), so a query matches the candidate only when the matcher can account for the predicate — never as if the index held every record.

This is where a predicate ENTERS the candidate machinery, and therefore where a predicate that provably filters NOTHING is resolved to no predicate at all. Sparseness is read back as `predicateProto != nil` in four separate places — the fan-out expansion arm, AbstractDataAccessRule's root-match restriction, candidatePreservesBaseRecordCardinality, and the flat expansion — and only the last converts the predicate before deciding. Classifying here is what keeps the other three from treating a COMPLETE index as filtered, which for the fan-out arm meant `WHERE TRUE` produced no candidate at all. Java gets the same effect structurally: the planner only ever sees an index predicate through IndexPredicate.toPredicate, whose folding constructors have already collapsed a tautology to the constant (IndexPredicate.java:344-345).

Only a PROVED tautology is dropped; anything unprovable stays sparse.

func (*ValueIndexScanMatchCandidate) WithPrimaryKeyComponentTypes

func (c *ValueIndexScanMatchCandidate) WithPrimaryKeyComponentTypes(types []values.Type) *ValueIndexScanMatchCandidate

WithPrimaryKeyComponentTypes attaches authoritative physical types aligned with GetPKColumnNames to a freshly constructed candidate.

func (*ValueIndexScanMatchCandidate) WithRecordTypeRowTypes

func (c *ValueIndexScanMatchCandidate) WithRecordTypeRowTypes(rowTypes []values.Type) *ValueIndexScanMatchCandidate

WithRecordTypeRowTypes attaches the per-record-type row layouts to a freshly constructed candidate (RFC-197 item 1). Call before the translate function is built. A nil/empty slice leaves the candidate with flowedType as its only layout — the single-record-type case, and fail-closed for a multi-type index.

func (*ValueIndexScanMatchCandidate) WithRootKeyExpression

WithRootKeyExpression attaches a caller-owned copy of the record-layer key expression to a freshly constructed candidate. A nil root clears the optional structure. Call this before GetTraversal; traversal construction is deliberately stable under sync.Once.

func (*ValueIndexScanMatchCandidate) WithValueColumns

WithValueColumns attaches the covering-only (FDB VALUE part) column names of a KeyWithValue-rooted index to a freshly constructed candidate. Names are upper-cased for the SQL-convention case-insensitive covering match, exactly like columnNames. Nil/empty clears the value part.

type ValuePredicateConstantFoldRule

type ValuePredicateConstantFoldRule struct {
	// contains filtered or unexported fields
}

ValuePredicateConstantFoldRule unwraps a ValuePredicate whose Value folds to a constant bool / null at plan time:

ValuePredicate{Value: ConstantValue(true)}    → ConstantPredicate(TriTrue)
ValuePredicate{Value: ConstantValue(false)}   → ConstantPredicate(TriFalse)
ValuePredicate{Value: NullValue}              → ConstantPredicate(TriUnknown)
ValuePredicate{Value: BooleanValue(true)}     → ConstantPredicate(TriTrue)
ValuePredicate{Value: BooleanValue(nil)}      → ConstantPredicate(TriUnknown)

Mirrors Java's `ConstantFoldingValuePredicateRule`. Without this rule, SimplifyPredicateValues collapses the Value tree but leaves the ValuePredicate wrapper — `(true AND something)` stays `AND(VP(true), something)` instead of folding to just `something` via the AndConstantSimplifyRule pass.

The rule fires AFTER SimplifyPredicateValues has run (or after other rules have folded the Value). It only operates on Values that IsConstantValue + EvaluateConstant accept; non-constant Values (FieldValue, ArithmeticValue with non-constant children) leave the wrapper alone.

Type-degraded inputs (Value evaluates to a non-bool literal — e.g. `ConstantValue(int64(1))` wrapped in ValuePredicate) fold to TriUnknown, matching ValuePredicate.Eval's runtime degradation behaviour. This prevents the wrapper surviving past simplification when the embedded executor would also report UNKNOWN at runtime.

func NewValuePredicateConstantFoldRule

func NewValuePredicateConstantFoldRule() *ValuePredicateConstantFoldRule

NewValuePredicateConstantFoldRule constructs the rule.

func (*ValuePredicateConstantFoldRule) Matcher

func (*ValuePredicateConstantFoldRule) OnMatch

func (r *ValuePredicateConstantFoldRule) OnMatch(call *RuleCall)

type VectorIndexScanMatchCandidate

type VectorIndexScanMatchCandidate struct {
	// contains filtered or unexported fields
}

VectorIndexScanMatchCandidate represents a vector similarity search index as a match candidate. Recognises K-nearest-neighbour queries driven by distance-based ranking predicates and maps them to a vector index scan.

Key differences from ValueIndexScanMatchCandidate:

  • Carries ordering aliases separately from sargable parameters (vector indexes have a different binding discipline).
  • Sargable aliases include partition key columns; a subset (parametersRequiredForBinding) must be bound.
  • The scan plan it produces uses a vector distance comparison.

Ports Java's `com.apple.foundationdb.record.query.plan.cascades.VectorIndexScanMatchCandidate`.

func NewVectorIndexScanMatchCandidate

func NewVectorIndexScanMatchCandidate(
	indexName string,
	recordTypes []string,
	columnNames []string,
	partitionCount int,
	metric values.DistanceOperator,
	flowedType values.Type,
	unique bool,
	primaryKeyColumns []string,
) *VectorIndexScanMatchCandidate

NewVectorIndexScanMatchCandidate constructs a match candidate for a vector similarity search index. columnNames are all index columns in key order (partition columns followed by the single vector column); partitionCount is the KeyWithValue split point; metric selects the distance function. The sargable aliases (one per partition column + one distance alias) and the required-for-binding set (the index-only distance alias) are minted here, mirroring Java's VectorIndexExpansionVisitor.

func (*VectorIndexScanMatchCandidate) CandidateName

func (c *VectorIndexScanMatchCandidate) CandidateName() string

CandidateName returns the index name.

func (*VectorIndexScanMatchCandidate) ComputeBoundParameterPrefixMap

ComputeBoundParameterPrefixMap collects the bound partition-prefix equality run plus the index-only DistanceRank binding. Unlike a value index, the two are kept separate: Java's toVectorIndexScanComparisons separates the distance rank by TYPE (instanceof DistanceRankValueComparison), never by prefix position, so the distance binding must survive even on a PARTIAL partition prefix (a multi-partition fan-out query). See RFC-046.

func (*VectorIndexScanMatchCandidate) CreatesDuplicates

func (c *VectorIndexScanMatchCandidate) CreatesDuplicates() bool

CreatesDuplicates reports whether the index can produce duplicate entries per record.

func (*VectorIndexScanMatchCandidate) EmitsOrderedStream

func (c *VectorIndexScanMatchCandidate) EmitsOrderedStream() bool

EmitsOrderedStream reports whether this candidate's ToScanPlan emits the VBASE distance-ordered (non-self-limiting) form — true for an un-partitioned index (global rank, RFC-156 Phase B). Such a scan must NOT be folded into a primary-key intersection: the residual composes as a Filter ABOVE the ordered stream (Limit → Filter → ordered scan), not as an intersection arm (which would reintroduce the predicate-subset-of-global-top-k wrong answer).

func (*VectorIndexScanMatchCandidate) GetBaseType

func (c *VectorIndexScanMatchCandidate) GetBaseType() values.Type

GetBaseType returns the base record type.

func (*VectorIndexScanMatchCandidate) GetColumnNames

func (c *VectorIndexScanMatchCandidate) GetColumnNames() []string

GetColumnNames returns the ordered column-name list.

func (*VectorIndexScanMatchCandidate) GetColumnSize

func (c *VectorIndexScanMatchCandidate) GetColumnSize() int

GetColumnSize returns the number of key columns.

func (*VectorIndexScanMatchCandidate) GetOrderingAliases

GetOrderingAliases returns the ordering aliases.

func (*VectorIndexScanMatchCandidate) GetPartitionKeyComponentTypes

func (c *VectorIndexScanMatchCandidate) GetPartitionKeyComponentTypes() []values.Type

GetPartitionKeyComponentTypes returns physical types aligned with the partition equality prefix.

func (*VectorIndexScanMatchCandidate) GetPrimaryKeyValues

func (c *VectorIndexScanMatchCandidate) GetPrimaryKeyValues() []values.Value

GetPrimaryKeyValues returns the primary key as a list of Values. Lazily computed. Returns nil if no PK columns.

func (*VectorIndexScanMatchCandidate) GetRecordTypes

func (c *VectorIndexScanMatchCandidate) GetRecordTypes() []string

GetRecordTypes returns the record types this index covers.

func (*VectorIndexScanMatchCandidate) GetSargableAliases

GetSargableAliases returns the sargable parameter aliases. Ports Java's VectorIndexScanMatchCandidate.getSargableAliases().

func (*VectorIndexScanMatchCandidate) GetSargableAliasesRequiredForBinding

func (c *VectorIndexScanMatchCandidate) GetSargableAliasesRequiredForBinding() []values.CorrelationIdentifier

GetSargableAliasesRequiredForBinding returns the parameter aliases that must be bound for the candidate to be valid.

func (*VectorIndexScanMatchCandidate) GetTraversal

func (c *VectorIndexScanMatchCandidate) GetTraversal() *Traversal

GetTraversal returns the Traversal of this candidate's expression tree, built lazily on first access.

func (*VectorIndexScanMatchCandidate) HasAndOrderedByRecordTypeKey

func (c *VectorIndexScanMatchCandidate) HasAndOrderedByRecordTypeKey() bool

HasAndOrderedByRecordTypeKey reports whether the index key starts with the record type key. Always false for vector indexes.

func (*VectorIndexScanMatchCandidate) IsUnique

func (c *VectorIndexScanMatchCandidate) IsUnique() bool

IsUnique reports whether the index enforces uniqueness.

func (*VectorIndexScanMatchCandidate) String

String returns a human-readable label for debugging. Mirrors Java's VectorIndexScanMatchCandidate.toString().

func (*VectorIndexScanMatchCandidate) ToScanPlan

ToScanPlan converts the matched bindings into a vector (BY_DISTANCE) scan plan. It separates the partition-key equality bindings (which form the HNSW partition prefix) from the single DistanceRank binding (which carries the query vector + k + ef_search). Ports Java's VectorIndexScanMatchCandidate.toEquivalentPlan / toVectorIndexScanComparisons.

func (*VectorIndexScanMatchCandidate) WithPartitionKeyComponentTypes

func (c *VectorIndexScanMatchCandidate) WithPartitionKeyComponentTypes(types []values.Type) *VectorIndexScanMatchCandidate

WithPartitionKeyComponentTypes attaches authoritative physical partition-key types to a freshly constructed vector candidate.

type Vectored

type Vectored[T any] struct {
	Value    T
	Position int
}

Vectored pairs a value with its position index. Used by the bit-sieve intersection logic to track which element came from which position in the original list.

Ports Java's AbstractDataAccessRule.Vectored<T>.

func MaximumCoverageMatches

func MaximumCoverageMatches(
	partialMatches []PartialMatch,
	requestedOrderings []*properties.RequestedOrdering,
	ctx PlanContext,
) []Vectored[*SingleMatchedAccess]

MaximumCoverageMatches eliminates PartialMatches whose coverage is entirely contained in other matches from the same MatchCandidate, then wraps survivors in Vectored with ascending position indices.

The Pareto filtering logic (findContainingAccess) prunes dominated matches: if match A from candidate C binds {x, y, z} and match B from the same candidate C binds {x, y}, then B is dominated by A (A covers everything B covers plus more) and B is pruned.

Ports Java's AbstractDataAccessRule.maximumCoverageMatches.

func NewVectored

func NewVectored[T any](value T, position int) Vectored[T]

NewVectored creates a Vectored with the given value and position.

func (Vectored[T]) String

func (v Vectored[T]) String() string

String returns a human-readable representation. Mirrors Java's Vectored.toString().

type WindowedIndexScanMatchCandidate

type WindowedIndexScanMatchCandidate struct {
	// contains filtered or unexported fields
}

WindowedIndexScanMatchCandidate represents a windowed (rank / leaderboard / time-series) index as a match candidate. Such indexes partition data by grouping keys, then order by a score within each group. Queries that bind the group columns and constrain the rank can be answered directly via a BY_RANK index scan.

Key differences from ValueIndexScanMatchCandidate:

  • Carries separate grouping aliases, a score alias, a rank alias, and primary key aliases. The sargable set is groupingAliases + [rankAlias]; the ordering set is groupingAliases + [scoreAlias] + primaryKeyAliases.
  • computeMatchedOrderingParts has special-case logic: when the current parameter is the scoreAlias, the comparison range is taken from the rankAlias binding instead, because binding rank by equality also determines the score.
  • computeOrderingFromScanComparisons treats the score ordinal specially — the score column is fixed with an opaque equality binding rather than the scan comparison.
  • Implements ScanWithFetchMatchCandidate (covering-index push-through).

Ports Java's `com.apple.foundationdb.record.query.plan.cascades.WindowedIndexScanMatchCandidate`.

func NewWindowedIndexScanMatchCandidate

func NewWindowedIndexScanMatchCandidate(
	indexName string,
	recordTypes []string,
	columnNames []string,
	groupingAliases []values.CorrelationIdentifier,
	scoreAlias values.CorrelationIdentifier,
	rankAlias values.CorrelationIdentifier,
	primaryKeyAliases []values.CorrelationIdentifier,
	indexKeyValues []values.Value,
	flowedType values.Type,
	unique bool,
	primaryKeyColumns []string,
) *WindowedIndexScanMatchCandidate

NewWindowedIndexScanMatchCandidate constructs a match candidate for a windowed (rank) index.

func (*WindowedIndexScanMatchCandidate) CandidateName

func (c *WindowedIndexScanMatchCandidate) CandidateName() string

CandidateName returns the index name.

func (*WindowedIndexScanMatchCandidate) ComputeBoundParameterPrefixMap

ComputeBoundParameterPrefixMap walks the sargable aliases and collects the longest prefix satisfying index scan discipline.

func (*WindowedIndexScanMatchCandidate) CreatesDuplicates

func (c *WindowedIndexScanMatchCandidate) CreatesDuplicates() bool

CreatesDuplicates reports whether the index can produce duplicate entries per record.

func (*WindowedIndexScanMatchCandidate) GetBaseType

func (c *WindowedIndexScanMatchCandidate) GetBaseType() values.Type

GetBaseType returns the base record type.

func (*WindowedIndexScanMatchCandidate) GetColumnNames

func (c *WindowedIndexScanMatchCandidate) GetColumnNames() []string

GetColumnNames returns the ordered column-name list.

func (*WindowedIndexScanMatchCandidate) GetColumnSize

func (c *WindowedIndexScanMatchCandidate) GetColumnSize() int

GetColumnSize returns the number of key columns.

func (*WindowedIndexScanMatchCandidate) GetGroupingAliases

GetGroupingAliases returns the grouping aliases.

func (*WindowedIndexScanMatchCandidate) GetIndexKeyValues

func (c *WindowedIndexScanMatchCandidate) GetIndexKeyValues() []values.Value

GetIndexKeyValues returns the index key Values.

func (*WindowedIndexScanMatchCandidate) GetOrderingAliases

GetOrderingAliases returns groupingAliases + [scoreAlias] + primaryKeyAliases. Ports Java's orderingAliases() static helper.

func (*WindowedIndexScanMatchCandidate) GetPrimaryKeyAliases

func (c *WindowedIndexScanMatchCandidate) GetPrimaryKeyAliases() []values.CorrelationIdentifier

GetPrimaryKeyAliases returns the primary key aliases.

func (*WindowedIndexScanMatchCandidate) GetPrimaryKeyValues

func (c *WindowedIndexScanMatchCandidate) GetPrimaryKeyValues() []values.Value

GetPrimaryKeyValues returns the primary key as a list of Values. Lazily computed. Returns nil if no PK columns.

func (*WindowedIndexScanMatchCandidate) GetRankAlias

GetRankAlias returns the rank alias.

func (*WindowedIndexScanMatchCandidate) GetRecordTypes

func (c *WindowedIndexScanMatchCandidate) GetRecordTypes() []string

GetRecordTypes returns the record types this index covers.

func (*WindowedIndexScanMatchCandidate) GetSargableAliases

GetSargableAliases returns groupingAliases + [rankAlias]. Ports Java's WindowedIndexScanMatchCandidate.getSargableAliases().

func (*WindowedIndexScanMatchCandidate) GetSargableAliasesRequiredForBinding

func (c *WindowedIndexScanMatchCandidate) GetSargableAliasesRequiredForBinding() []values.CorrelationIdentifier

GetSargableAliasesRequiredForBinding returns nil — windowed indexes do not require any specific aliases to be bound.

func (*WindowedIndexScanMatchCandidate) GetScoreAlias

GetScoreAlias returns the score alias.

func (*WindowedIndexScanMatchCandidate) GetTraversal

func (c *WindowedIndexScanMatchCandidate) GetTraversal() *Traversal

GetTraversal returns the Traversal of this candidate's expression tree, built lazily on first access.

func (*WindowedIndexScanMatchCandidate) HasAndOrderedByRecordTypeKey

func (c *WindowedIndexScanMatchCandidate) HasAndOrderedByRecordTypeKey() bool

HasAndOrderedByRecordTypeKey reports whether the index key starts with the record type key. Always false for windowed indexes.

func (*WindowedIndexScanMatchCandidate) IsUnique

func (c *WindowedIndexScanMatchCandidate) IsUnique() bool

IsUnique reports whether the index enforces uniqueness.

func (*WindowedIndexScanMatchCandidate) PushValueThroughFetch

func (c *WindowedIndexScanMatchCandidate) PushValueThroughFetch(
	value values.Value,
	sourceAlias values.CorrelationIdentifier,
	targetAlias values.CorrelationIdentifier,
) (values.Value, bool)

PushValueThroughFetch attempts to translate a value from the full-record domain to the index-entry domain. Returns the translated value and true on success; nil and false otherwise. Implements ScanWithFetchMatchCandidate.

func (*WindowedIndexScanMatchCandidate) String

String returns a human-readable label for debugging. Mirrors Java's WindowedIndexScanMatchCandidate.toString().

func (*WindowedIndexScanMatchCandidate) ToScanPlan

ToScanPlan converts the matched prefix into a physical plan. Produces a RecordQueryIndexPlan (BY_RANK scan) wrapped in a FetchFromPartialRecordPlan.

Ports Java's WindowedIndexScanMatchCandidate.toEquivalentPlan().

type WithBaseQuantifierMatchCandidate

type WithBaseQuantifierMatchCandidate interface {
	MatchCandidate
	// GetBaseType returns the base record type for this candidate.
	GetBaseType() values.Type
}

WithBaseQuantifierMatchCandidate is a MatchCandidate defined using a base quantifier (i.e., not a true join index). Ports Java's `com.apple.foundationdb.record.query.plan.cascades.WithBaseQuantifierMatchCandidate`.

type WithChildren

type WithChildren interface {
	// WithChildren returns a fresh expression of the same concrete
	// type, using the supplied quantifiers in place of the originals.
	// Returns an error if the quantifier count or shape doesn't match
	// what the type expects.
	WithChildren(qs []expressions.Quantifier) (expressions.RelationalExpression, error)
}

WithChildren is the optional interface a RelationalExpression implements to support generic plan extraction. ExtractBestPlan's default arm calls WithChildren(freshChildren) to construct a fresh expression of the same concrete type with the supplied quantifiers.

The concrete RelationalExpression types do NOT implement WithChildren — their constructors take operator-specific args (predicates, sort keys, etc.) that the generic rebuilder doesn't have. Instead, ExtractBestPlan has explicit switch arms for those types. The interface exists for OPAQUE wrappers (e.g. cascades- internal physical-plan adapters) that want to participate in extraction without forcing a switch-arm extension.

Mirrors Java's `RelationalExpressionWithChildren.withNewChildren` for that wrapper subset.

type WithPrimaryKeyMatchCandidate

type WithPrimaryKeyMatchCandidate interface {
	MatchCandidate
	// GetPrimaryKeyValues returns the primary key values for this
	// candidate, or nil if the primary key cannot be computed.
	// Ports Java's getPrimaryKeyValuesMaybe() — nil corresponds to
	// Optional.empty().
	GetPrimaryKeyValues() []values.Value
}

WithPrimaryKeyMatchCandidate is a MatchCandidate that uses a primary key to identify a record. Ports Java's `com.apple.foundationdb.record.query.plan.cascades.WithPrimaryKeyMatchCandidate`.

Source Files

Directories

Path Synopsis
Package expressions ports the Cascades-side relational expression hierarchy from Java's `com.apple.foundationdb.record.query.plan.cascades.expressions`.
Package expressions ports the Cascades-side relational expression hierarchy from Java's `com.apple.foundationdb.record.query.plan.cascades.expressions`.
Package properties — Cardinality and Cardinalities types.
Package properties — Cardinality and Cardinalities types.
Package values is the Value-tier of the Go Cascades planner port — scalar / row-context expressions that compose into predicates, projections, and join keys.
Package values is the Value-tier of the Go Cascades planner port — scalar / row-context expressions that compose into predicates, projections, and join keys.

Jump to

Keyboard shortcuts

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