properties

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: 7 Imported by: 0

Documentation

Overview

Package properties — Cardinality and Cardinalities types.

Ports Java's CardinalitiesProperty.Cardinality and CardinalitiesProperty.Cardinalities 1:1. A Cardinality is a single bound (known int64 or unknown); Cardinalities is a min/max pair.

The merge helpers (IntersectCardinalities, UnionCardinalities, WeakenCardinalities) match Java's visitor-private methods exactly, including their unknown-handling semantics.

This file also retains the old EstimateCardinality helpers that wrap the Cost-walk. The new Cardinalities type is a SEPARATE property computed on physical plans (see plan_properties.go); the old EstimateCardinality is a heuristic on logical expressions. Both coexist — consumers pick whichever is appropriate.

Package properties is Cascades' per-RelationalExpression derived-property machinery — the "decision support" the planner uses to pick a single best plan from an equivalence class of equivalent expressions.

Cost model (this file): per RFC-024 the Go cost model is Go-native (NOT hash-identical to Java) — the goal is "pick a sensible cheaper plan among the rule-generated alternatives", not "match Java's plan-cache key bit-for-bit". Cost covers cardinality + a per-operator CPU heuristic; the package's other files derive the remaining per-expression properties (ordering, cardinalities, comparisons, record types, plan-partition properties, …), each modeled on its counterpart in Java's `properties/` package.

Design choices captured:

  • Cost is computed on demand by walking the expression tree, NOT attached to expressions. Java caches cost on the equivalence class; Go re-computes per call, with per-call memoisation on the walks that revisit shared sub-References (estimateCostMemoised / BestRefCostWith / BestMemberCostWith).

  • Sub-Reference recursion in EstimateCostWith picks the FIRST exploratory member's cost, not the cheapest, and falls back to the first final member only for a finals-only pinned Reference. Two reasons: (a) recursion through the cheapest is well-defined for a DAG but exponential without memoisation; (b) exploration rules only ADD members — Reference.Insert appends, the original input expression stays at index 0 — so this costs the unoptimised sub-tree consistently across siblings, which is what "compare members at THIS Reference" wants. Caveat: index 0 is stable under rule yields but NOT under RFC-037 cross-group memo merges, which re-point the canonical member list — see FuzzCostSanity for the consequence (first-member cost of an unchanged parent can shift after a merge). The planner's winner stamping + selector-driven extraction (ExtractBestPlanFromSelector) is the production path that avoids re-costing; when child costing moves to winner-based (BestMemberCostWith), the best-cost monotonicity pin comes back (RFC-175 §2 A2).

  • Tunable constants are package-level. Calibration target: a Filter under a Sort should beat a Sort under a Filter (push-Filter-through-Sort = cheaper); a Distinct directly over a Sort should beat a Distinct over an unsorted scan that would need its own sort (DistinctOverSortElim picks the no-sort path); Union of cheap children should beat Intersection (the latter scans every child end-to-end).

Package properties — DerivationsProperty file.

DerivationsProperty computes value derivation information for plan nodes — tracking which values flow where through the plan tree. A derivation is a Value-tree that is not correlated to anything. This is achieved by exhaustively inlining the producer of a correlation into the consumer.

Ports Java's `com.apple.foundationdb.record.query.plan.cascades.properties.DerivationsProperty`.

In particular, a Value-tree that appears somewhere in a plan as part of a QueryPredicate or other expression is guaranteed to be part of a derivation which is not correlated.

Index

Constants

View Source
const (
	// LeafScanCardinality is the cardinality estimate for a
	// FullUnorderedScan. Without table statistics the planner can't
	// know the row count; a fixed large constant keeps per-plan
	// comparison picking the plan with fewer / cheaper post-scan
	// operators.
	LeafScanCardinality = 1e6

	// FilterSelectivity is the fraction of rows a Filter retains by
	// default. 0.5 is a common heuristic when no per-predicate
	// selectivity is known. ProjectionMerge / DistinctOverSortElim
	// compare against this to pick rule outputs.
	FilterSelectivity = 0.5

	// RangeSelectivity is the fraction of rows a range predicate
	// (col > X, col < Y, BETWEEN) retains. 0.33 assumes roughly
	// one-third of the value domain is selected.
	RangeSelectivity = 0.33

	// EqualityBoundSelectivity is the fraction of rows an EQUALITY index-scan
	// bound (col = X) retains. A point match selects far fewer rows than a range,
	// so this MUST be < RangeSelectivity — otherwise the cost model treats an
	// equality probe as less selective than a range probe and picks the wrong
	// index (RFC-164 COST-SELECTIVITY). It is deliberately distinct from
	// FilterSelectivity (0.5, the generic residual-Filter heuristic): a residual
	// filter of unknown form may keep half the rows, but an indexed equality bound
	// is a point lookup. The invariant EqualityBoundSelectivity < RangeSelectivity
	// is pinned by TestCostSelectivity_EqualityBeatsRange.
	EqualityBoundSelectivity = 0.1

	// DistinctSelectivity is the fraction of rows Distinct retains
	// (i.e. the assumed unique rate). 0.7 means 30% of input rows are
	// duplicates of an earlier row.
	DistinctSelectivity = 0.7

	// TypeFilterSelectivity is the fraction of rows a TypeFilter
	// retains. 0.5 is a rough "two type universes" assumption.
	TypeFilterSelectivity = 0.5

	// Per-operator CPU constants. CPU is in row-equivalent units so
	// cardinality and CPU share a scale (Total = Cardinality + CPU).
	FilterCPU       = 0.1
	ProjectionCPU   = 0.05
	SortCPU         = 0.15 // multiplied by N * log2(N) for Nlog(N) sort
	DistinctCPU     = 1.5
	TypeFilterCPU   = 0.05
	UnionCPU        = 0.1
	IntersectionCPU = 1.0
	SelectCPU       = 0.5
	WriteCPU        = 1.0
	// FetchCPU is the cost of ONE ISOLATED random single-key round trip: a
	// Get, or a GetRange bound to exactly one row when that call is NOT
	// amortized across a shared streaming iterator returning many rows.
	// Two executor paths verified to have this exact shape:
	//   - RecordQueryFetchFromPartialRecordPlan: split_helper.go's
	//     loadWithSplit does one blocking tx.Get(unsplitKey).Get() per index
	//     entry (the common unsplit-record case), called once per resolved
	//     entry with NO batching across entries (flat_map_cursor.go's own
	//     comment: "Go simplification: no async pipelining").
	//   - A full-PK or full-unique-index EQUALITY point-probe reached as an
	//     ISOLATED probe (a FlatMap's per-outer-row inner, or a standalone
	//     `WHERE pk = ?`): key_value_cursor.go's initIterator issues its OWN
	//     tx.GetRange bound to that one row, and flatMapCursor.OnNext opens a
	//     FRESH inner cursor per outer row via ExecutePlan with no
	//     cross-row pipelining (same file/comment as above) — so this
	//     GetRange is exactly as isolated, unbatched, and unamortized as the
	//     Fetch's Get. Charging the AMORTIZED sequential-scan rate (ScanCPU,
	//     below) for it was pricing the same physical round trip two
	//     different ways depending on which plan node issued it.
	FetchCPU = 1.5
	// PhysicalRangeSeekCost is the fixed setup charged for each additional
	// physical range opened by one logical equality leaf. The first range is
	// already represented by the leaf's existing fetch/scan work; plural range
	// sets add this unit for every subsequent seek. It deliberately matches the
	// isolated-round-trip rate rather than the amortized per-row ScanCPU rate.
	PhysicalRangeSeekCost = FetchCPU
	// StreamingAggCPU is cheaper than DistinctCPU (no hash table, pre-sorted input).
	StreamingAggCPU = 1.2
	// ScanCPU is the AMORTIZED per-row cost of a genuine multi-row streaming
	// range read (a full table/index scan, or any bound that leaves more
	// than one row un-eliminated): the single tx.GetRange call's
	// round-trip cost is spread over however many rows the SAME call
	// streams back, so each row's marginal cost is small. This is NOT the
	// rate for an isolated single-row probe — see FetchCPU. Kept < FilterCPU
	// so a bare scan is never costed as a filtered scan.
	ScanCPU = 0.05

	// IterationOverhead is the fixed per-outer-row cost a dependent (FlatMap)
	// join pays to RE-EXECUTE its inner once per outer row: open the inner
	// cursor, initialize the range read, and bind the correlation. A 200-row
	// driver pays 10x the per-iteration overhead of a 20-row driver, so this term
	// expresses the genuine "drive the nested loop from the SMALLER side"
	// preference among two full-scan drivers — the case where Java's
	// CardinalitiesProperty (Go's criterion #2) ABSTAINS because both roots are
	// unbounded/unknown. It is a Go-only STATS read-side extension (RFC-041/042),
	// NOT a Java port: it lives only inside the Go-only compareJoinOrdering cost
	// path (flatMapCost), never in criterion #2. Sized as a TIE-BREAKER — small
	// enough never to flip a clear cardinality/total-cost winner, large enough to
	// tip a genuinely-close join-order tie toward fewer inner re-executions.
	// 0.1 == FilterCPU: one extra row-equivalent of setup per iteration.
	IterationOverhead = 0.1

	// MaxFiniteHeuristic is the saturation ceiling for planner-only arithmetic.
	// It is intentionally far below MaxFloat64 so adding two saturated Cost
	// axes remains finite and the total-cost preorder never sees NaN or Inf.
	MaxFiniteHeuristic = float64(math.MaxInt64)
)

Tunable constants. Calibrated to give plausible orderings for the default rule sets (DefaultExpressionRules + the PLANNING-phase rules); not measured against real workloads.

View Source
const PhysicalWrapperCostMultiplier = 0.9

PhysicalWrapperCostMultiplier is applied to each physical operator's inherited CPU so cost-driven extraction prefers physical plans over their logical counterparts. 0.9 = "physical is 10% cheaper than logical" — enough to flip ordering on equally-shaped alternatives.

CPU-ONLY. It must NEVER scale Cardinality. Cardinality is a property of the LOGICAL group — every physical implementation of the same join/filter/etc. produces the same number of rows — so it cannot legitimately shrink just because a plan happens to wrap its child in one more physical node than an alternative does. CPU is different: each additional physical operator is a genuine additional step at execution time, so a per-node execution-overhead discount is a coherent (if approximate) model there.

This WAS applied to Cardinality throughout this file and in plan/plans/cost.go (compounding once per physical node in a plan's depth), and it was NOT "small enough not to dominate the cost comparison with structurally-different alternatives" as this comment used to claim: measured directly, comparing a materialized NestedLoopJoin (one extra Fetch/Filter-free inner) against a re-scanning FlatMap whose inner carries one more wrapper node (PredicatesFilter/DefaultOnEmpty) made the FlatMap's Cardinality ~10% cheaper for IDENTICAL true selectivity — a swing bigger than the entire CPU term the two shapes otherwise differ by, so it silently decided compareJoinOrdering by tree shape instead of cost (the RFC-152 preserved-only regression). Confining the multiplier to CPU makes two physical realizations of the same logical join carry EXACTLY EQUAL Cardinality, so Cost.Less falls through to CPU — which is where the real discriminator (materialize-once vs re-scan-per-row) already lives.

Variables

View Source
var (
	PropOrdering = &ExpressionProperty{name: "ordering"}
	// PropRichOrdering is the FULL ordering — binding map, ordering set and
	// distinctness — which is what Java's OrderingProperty.ordering() carries
	// and what a rule needs to build a merge comparison key. PropOrdering is a
	// LOSSY projection of it (key sequence plus direction/null flags only).
	//
	// Both are computed per plan, so a rule that reads the rich form must roll
	// its partitions up to THIS property; rolling up to PropOrdering leaves two
	// members with equal key sequences but different binding maps in one
	// partition, which is heterogeneity on a property the rule reads.
	PropRichOrdering    = &ExpressionProperty{name: "richOrdering"}
	PropDistinctRecords = &ExpressionProperty{name: "distinctRecords"}
	PropStoredRecord    = &ExpressionProperty{name: "storedRecord"}
	PropPrimaryKey      = &ExpressionProperty{name: "primaryKey"}
	PropCardinalities   = &ExpressionProperty{name: "cardinalities"}

	PropComparisons               = &ExpressionProperty{name: "comparisons"}
	PropExpressionCount           = &ExpressionProperty{name: "expressionCount"}
	PropFieldWithComparisonCount  = &ExpressionProperty{name: "fieldWithComparisonCount"}
	PropPredicateComplexity       = &ExpressionProperty{name: "predicateComplexity"}
	PropPredicateCountByLevel     = &ExpressionProperty{name: "predicateCountByLevel"}
	PropRecordTypes               = &ExpressionProperty{name: "recordTypes"}
	PropReferencesAndDependencies = &ExpressionProperty{name: "referencesAndDependencies"}
	PropUsedTypes                 = &ExpressionProperty{name: "usedTypes"}
	PropDerivations               = &ExpressionProperty{name: "derivations"}

	AllPlanProperties = []*ExpressionProperty{
		PropOrdering, PropRichOrdering, PropDistinctRecords, PropStoredRecord, PropPrimaryKey,
		PropCardinalities,
		PropComparisons, PropExpressionCount, PropFieldWithComparisonCount,
		PropPredicateComplexity, PropPredicateCountByLevel, PropRecordTypes,
		PropReferencesAndDependencies, PropUsedTypes, PropDerivations,
	}
)
View Source
var LogicalCardinalityArms = []string{
	"*expressions.FullUnorderedScanExpression",
	"*expressions.LogicalFilterExpression",
	"*expressions.LogicalProjectionExpression",
	"*expressions.LogicalSortExpression",
	"*expressions.LogicalDistinctExpression",
	"*expressions.LogicalTypeFilterExpression",
	"*expressions.LogicalUnionExpression",
	"*expressions.LogicalIntersectionExpression",
	"*expressions.SelectExpression",
	"*expressions.GroupByExpression",
	"*expressions.InsertExpression",
	"*expressions.UpdateExpression",
	"*expressions.DeleteExpression",
	"*expressions.LogicalValuesExpression",
	"*expressions.LogicalLimitExpression",
	"*expressions.LogicalUniqueExpression",
	"*expressions.RecursiveUnionExpression",
}

LogicalCardinalityArms names every logical expression type provenLogicalCardinalities has an arm for.

Nothing else enumerates that switch, so the logical/physical pairing table had no way to notice an arm nobody had paired — it could only fail on entries someone had already thought to write down, which is the vacuous-pass shape this RFC's own drift channel displaced one level up. The parity test walks this list and requires an entry for each name.

Adding an arm above without adding it here leaves the arm unpaired and untested; adding it here without an arm makes the parity test fail on the missing derivation. Either way the omission is loud.

Functions

func AnyEqualityWidensBeyondOneKey

func AnyEqualityWidensBeyondOneKey(
	comps []*predicates.ComparisonRange,
	physicalTypes []values.Type,
) bool

AnyEqualityWidensBeyondOneKey reports whether any equality in comps binds MORE THAN ONE physical key, so the comparisons do NOT pin a single index entry even when every column is equality-bound.

The only such case today is a zero-valued FLOAT/DOUBLE equality. IEEE says -0.0 == +0.0, while FDB tuple encoding preserves the sign bit, so the two are distinct adjacent keys and the executor widens a zero bound to span both.

This is the SINGLE home for that exception. It previously lived nowhere and four independent point-probe proofs each concluded "unique + all equalities = exactly one row" on their own — computeCardinalities, isProvablePointProbe, indexProvableMaxCard and scanLikeCost — so fixing one left the same plan with contradictory cardinality and the cost model still ranking on the false bound.

Conservative on purpose for a NON-CONSTANT float operand: its runtime value is unknown, and if it binds to zero the scan widens. Callers that care about SARGABILITY rather than a proof want the stricter constant-only test instead (see match_candidate_index), because being conservative there de-sargs a probe into a full leading-column scan rather than merely costing a sort.

func AreAllBindingsFixed

func AreAllBindingsFixed(bindings []OrderingBinding) bool

AreAllBindingsFixed returns true if all bindings are fixed (equality-bound).

func BestRefCardinality

func BestRefCardinality(ref *expressions.Reference) float64

BestRefCardinality returns the cardinality of the cheapest member in a Reference — wraps BestRefCost.

func BoundSelectivity

func BoundSelectivity(comps []*predicates.ComparisonRange) (sel float64, numBound int, allEquality bool)

BoundSelectivity returns the combined selectivity of a scan's bound comparison prefix, how many columns are bound, and whether every bound column is an equality. Callers layer their own unique / point-lookup short-circuit on top.

Each equality bound multiplies in EqualityBoundSelectivity and each open range multiplies in RangeSelectivity. A point probe is more selective than a range, so EqualityBoundSelectivity < RangeSelectivity (RFC-164 COST-SELECTIVITY); using the generic residual FilterSelectivity (0.5) for an equality bound inverted that and mis-picked the index. Empty/nil bounds are skipped.

This is the SINGLE source for equality-vs-range bound costing, shared by RecordQueryScanPlan.HintCost, RecordQueryIndexPlan.HintCost, and scanLikeCost. It is centralised deliberately: the same loop duplicated across those sites is how the inverted equality cost survived (in dead code) past the original fix.

LOW-NDV CAVEAT: statless, this assumes every equality is a high-cardinality point (NDV≈10). A low-NDV equality (e.g. `status = ?`, a boolean) actually retains far more than 10% — costing it cheaper than it is. Distinguishing that needs per-column NDV statistics (not yet available here); see scanLikeCost's fullBindUnique note.

func CardinalityLess

func CardinalityLess(a, b expressions.RelationalExpression) bool

CardinalityLess is a Reference.GetBest-compatible comparator that orders members by cardinality alone (ignoring CPU). Useful when the planner wants to pick the smallest-output member regardless of compute cost — e.g. picking the join-build side for a hash join.

Ties are broken by member identity (first-appearance wins) — same stability contract as CostLess.

func ClampCardinality

func ClampCardinality(estimate float64, bounds Cardinalities) float64

ClampCardinality returns estimate constrained to the interval bounds proves, clamping per bound and ONLY where that bound is known.

Zero is preserved deliberately. The floor applies only when the proof says min >= 1, so a proven-zero or possibly-zero child keeps its zero and FlatMapCost's LIMIT-0 contract survives untouched: `SELECT * FROM t1, (SELECT * FROM t2 LIMIT 0)` must cost as the free empty join it is, not be floored into a phantom row that propagates multiplicatively through the join tree.

An unknown bound constrains nothing — the property abstaining is not a claim that the estimate is wrong, so the estimate passes through untouched on that side. A NEGATIVE estimate is not a row count and is raised to zero even with no proven min: no formula in this package produces one, and letting it through would make a subtree's cost cancel a sibling's under a summing parent.

func ComparePredicateCountByLevel

func ComparePredicateCountByLevel(a, b PredicateCountByLevelInfo) int

ComparePredicateCountByLevel compares two PredicateCountByLevelInfo instances level by level, starting from the deepest (leaf) level. Returns negative if a has fewer predicates at a deeper level (or fewer levels), positive if more, and zero if equal at all levels. Matches Java's PredicateCountByLevelInfo.compare.

func ComparisonRangeHasUnsupportedKnownNaN

func ComparisonRangeHasUnsupportedKnownNaN(
	cr *predicates.ComparisonRange,
	physicalType values.Type,
) bool

ComparisonRangeHasUnsupportedKnownNaN reports whether a scan comparison contains a plan-time-known NaN that would be encoded on a FLOAT or DOUBLE physical key. This deliberately covers ordered comparisons as well as equality. FDB tuple order preserves NaN sign and payload regions, while the predicate comparator treats all NaNs as one total-order value, so no single raw-payload endpoint is an exact logical bound.

Ordinary value/primary candidates use this result to leave the comparison as residual compensation. Physical leaves that cannot compensate (aggregate indexes and self-limiting vector partitions) decline the candidate entirely.

func CostLess

func CostLess(a, b expressions.RelationalExpression) bool

CostLess returns a comparator function `less(a, b RelationalExpression) bool` driven by EstimateCost with DefaultStatistics. Suitable as the Reference.GetBest argument.

Stable across calls (no internal state). The closure captures nothing — exposed as a function so future callers can swap in a different cost model without changing the GetBest signature.

func CostLessWith

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

CostLessWith returns a comparator function bound to the given StatisticsProvider. Use this when extracting plans against a known table-stats environment — Reference.GetBest receives a pure function, so the stats are baked into the closure.

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

func EqualityBoundsCoverKey

func EqualityBoundsCoverKey(comps []*predicates.ComparisonRange, keyColumnCount int) bool

EqualityBoundsCoverKey reports whether the comparison prefix proves that every column of a key with keyColumnCount columns is equality-bound.

Scan comparisons contain only the bound prefix, so "every comparison that happens to be present is an equality" does not prove full coverage of a composite key. Unknown key arity (keyColumnCount <= 0), nil/empty gaps, and ranges all fail closed. More comparisons than the key arity are tolerated only when every one is a non-empty equality; primary-scan construction should not normally produce that shape, but full key coverage still holds.

func EstimateCardinality

func EstimateCardinality(e expressions.RelationalExpression) float64

EstimateCardinality returns just the cardinality (row count estimate) of an expression — wraps the Cost-walk and projects out the CPU axis. Useful for cardinality-aware rules / matchers that don't need to drag the CPU calculation through.

O(N) over the expression sub-tree (same complexity as EstimateCost). Sub-Reference recursion picks the first member, matching EstimateCost's policy.

func EstimateCardinalityWith

func EstimateCardinalityWith(e expressions.RelationalExpression, stats StatisticsProvider) float64

EstimateCardinalityWith uses a custom StatisticsProvider for per-record-type cardinality (e.g. via Catalog lookups). Wraps EstimateCostWith.

func EvaluateExpressionCount

func EvaluateExpressionCount(expr expressions.RelationalExpression, filter func(expressions.RelationalExpression) bool) int

EvaluateExpressionCount returns the number of RelationalExpression nodes in the tree rooted at expr. Matches Java's ExpressionCountProperty.evaluate — a bottom-up sum where each node contributes 1 if it passes the filter predicate.

The filter parameter lets callers restrict the count to specific expression types (matching Java's ofTrackedTypes pattern). Pass nil to count all expressions.

func EvaluateFieldWithComparisonCount

func EvaluateFieldWithComparisonCount(expr expressions.RelationalExpression) int

EvaluateFieldWithComparisonCount walks the expression tree and counts the number of comparison predicates that appear in filter expressions. Matches Java's FieldWithComparisonCountProperty.evaluate — in Java this counts FieldWithComparison nodes inside RecordQueryFilterPlan's QueryComponent tree. Go's architecture uses ComparisonPredicate instead, so we count those within expressions that implement RelationalExpressionWithPredicates.

At references with multiple members, Java takes the minimum across members (the most optimistic plan). This evaluator takes the sum across all members for simplicity; the minimum-at-ref semantic can be added when plan selection depends on it.

func EvaluatePredicateComplexity

func EvaluatePredicateComplexity(expr expressions.RelationalExpression) int

EvaluatePredicateComplexity walks the expression tree and returns the maximum "diameter" (widest level in the tree) of any predicate attached to a RelationalExpressionWithPredicates node. Matches Java's PredicateComplexityProperty.evaluate, which computes TreeLike.diameterWithLevelCounting for each predicate and returns the max across the entire expression tree.

A higher value means more complex predicate structure. Zero means no predicates exist in the tree.

func EvaluateRecordTypes

func EvaluateRecordTypes(expr expressions.RelationalExpression) map[string]struct{}

EvaluateRecordTypes walks the expression tree bottom-up and collects the set of record type names the expression could produce. Matches Java's RecordTypesProperty.evaluate.

Semantics:

  • Leaf nodes that implement RecordTypeProvider contribute their record types directly.
  • TypeFilter expressions (which also implement RecordTypeProvider) contribute only their filtered type set.
  • Non-leaf expressions with a single child propagate that child's record types unchanged.
  • Multi-child expressions (union, intersection, select, etc.) union all children's record types.
  • Unknown leaf nodes contribute nothing.

func EvaluateUsedTypes

func EvaluateUsedTypes(expr expressions.RelationalExpression) []values.Type

EvaluateUsedTypes walks the expression tree bottom-up and collects all dynamic (complex) Type objects used anywhere in the tree. Matches Java's UsedTypesProperty.evaluate.

Expressions that implement DynamicTypeProvider contribute their types directly; all children's types are unioned.

func HasMultipleFixedBindings

func HasMultipleFixedBindings(bindings []OrderingBinding) bool

HasMultipleFixedBindings returns true if there are 2+ fixed bindings.

func IsCorrelatedTo

func IsCorrelatedTo(v values.Value, alias values.CorrelationIdentifier) bool

IsCorrelatedTo reports whether a value tree references the given alias. Go equivalent of Java's Value.isCorrelatedTo(alias).

func IsOrdered

IsOrdered reports whether the expression has a known output order. Convenience wrapper over EstimateOrdering.

func LogicalEqualityAtMostOnePhysicalKey

func LogicalEqualityAtMostOnePhysicalKey(
	cr *predicates.ComparisonRange,
	physicalType values.Type,
) bool

LogicalEqualityAtMostOnePhysicalKey reports whether evaluating cr as a row-level logical equality over a UNIQUE physical key can match at most one stored key. This is intentionally a different proof from PhysicalEqualityShapeForComponent.

A scan range has an exact-or-loud binder: for example, a FLOAT/DOUBLE comparand over an integer key is rejected at runtime when its cmpAny equality class contains several integers. A residual filter or materialized nested-loop join has no such binder. It evaluates cmpAny against every row, so raw physical uniqueness is useful only when the complete logical equality class is provably a singleton (or empty).

The two important non-singleton classes are:

  • both signed-zero encodings and every raw NaN encoding in a physical FLOAT/DOUBLE key; and
  • adjacent physical integers that round to one FLOAT/DOUBLE comparand at the binary64 precision cliff.

Unknown physical metadata and dynamic shapes that can reach either class fail closed. A constant non-zero, non-NaN floating equality remains useful: at most one physical FLOAT/DOUBLE encoding has that logical value.

func LogicalEqualityProjectionInjective

func LogicalEqualityProjectionInjective(sourceType, physicalTargetType values.Type) bool

LogicalEqualityProjectionInjective reports whether distinct values of a source key are guaranteed to induce disjoint equality probes in the stated physical target domain. It is stronger than LogicalEqualityAtMostOnePhysicalKey: the latter bounds one probe's result; this one proves that the results of many correlated probes cannot overlap.

Floating source keys always decline because distinct physical signed-zero and NaN encodings are already one logical equality value. Integer-to-FLOAT narrowing and LONG-to-DOUBLE conversion also decline because distinct integers can round to one target value. INT-to-DOUBLE is injective over the INT/uint32 domain; integer tuple carriers are mutually exact.

func NaturalComparisonKeyValues

func NaturalComparisonKeyValues(parts []ProvidedOrderingPart, reverse bool) ([]values.Value, bool)

NaturalComparisonKeyValues maps ordering parts to executable raw comparison Values. ok=false means at least one part requires ordered-byte encoding and the caller must decline the plan until that evaluator exists.

func PartsEqual

func PartsEqual(a, b []RequestedOrderingPart) bool

PartsEqual reports whether two requested-ordering part lists are equal element-wise: same Value (structural equality) and same sort order. Mirrors Java's List<RequestedOrderingPart>.equals in RequestedOrderingConstraint.combine.

func PhysicalKeyOrderingCongruent

func PhysicalKeyOrderingCongruent(
	cr *predicates.ComparisonRange,
	physicalType values.Type,
) bool

PhysicalKeyOrderingCongruent reports whether FDB tuple order at one key coordinate is congruent with the query comparator for the rows selected by cr. It does NOT say whether the following coordinates are also ordered; use PhysicalOrderingPrefixLength, which honours terminality.

func PhysicalOrderingPrefixEndsTerminal

func PhysicalOrderingPrefixEndsTerminal(
	comps []*predicates.ComparisonRange,
	physicalTypes []values.Type,
	n int,
) bool

PhysicalOrderingPrefixEndsTerminal reports whether the usable ordering prefix stops because a coordinate was TERMINAL rather than because it ran out of key.

Callers that extend an ordering PAST the key — an index scan appending its trimmed primary-key suffix, say — must consult this. "I used every index column" is not the same as "the ordering continues": a float coordinate relaxed through the NaN tie class is usable AND final, so a suffix appended after it is inside the tie and comes back in NaN-payload order.

func PhysicalOrderingPrefixLength

func PhysicalOrderingPrefixLength(
	comps []*predicates.ComparisonRange,
	physicalTypes []values.Type,
	n int,
) int

func SaturatingHeuristicAdd

func SaturatingHeuristicAdd(a, b float64) float64

SaturatingHeuristicAdd adds non-negative planner heuristics without producing NaN or infinity. Invalid or overflowing inputs conservatively saturate to MaxFiniteHeuristic.

func SaturatingHeuristicMultiply

func SaturatingHeuristicMultiply(a, b float64) float64

SaturatingHeuristicMultiply multiplies non-negative planner heuristics without producing NaN or infinity.

func SecondaryUniqueKeyEnforcedOnStream

func SecondaryUniqueKeyEnforcedOnStream(
	physicalTypes []values.Type, keyColumnCount int, nullRejectedByStream []bool,
) bool

SecondaryUniqueKeyEnforcedOnStream is the same proof restricted to ONE STREAM rather than to every possible one, and it is the difference between a fact about the catalog and a fact about the rows actually arriving.

The exempt set of a UNIQUE index is the entries on which the declaration constrains nothing, or constrains something finer than logical row equality: NULL components (under NULLS DISTINCT the uniqueness check is SKIPPED, so one NULL prefix legitimately holds arbitrarily many entries) and raw-NaN components (distinct tuple encodings that the dedup key canonicalizes to one value). The proof a consumer needs is that the exempt set is EMPTY here.

nullRejectedByStream[i] asserts that no row reaching the consumer can carry a NULL in key component i — established upstream by a NULL-rejecting predicate, never by this function. Where it is true, the component's declared nullability is no longer the deciding fact and the metadata clause is satisfied on this stream. A nil or short slice asserts nothing, which is what makes the zero-argument form above the strictly stronger catalog-wide question.

The FLOAT/DOUBLE half is deliberately NOT overridable. A predicate that rejects NULL says nothing about NaN — `d IS NOT NULL` admits every NaN encoding — so the raw-NaN half of the exempt set survives any NULL rejection and TupleKeyUniquenessMatchesLogicalEquality stays absolute.

func SecondaryUniqueKeyGloballyEnforced

func SecondaryUniqueKeyGloballyEnforced(physicalTypes []values.Type, keyColumnCount int) bool

SecondaryUniqueKeyGloballyEnforced reports whether a secondary UNIQUE declaration is a true uniqueness invariant over every stored index entry. Under the engine's NULLS DISTINCT policy, that additionally requires authoritative NOT NULL metadata for every indexed key component. This stronger global proof is used for DISTINCT elimination and strict-ordering claims; point probes use ProvenFullEqualityMultiplicity, which can still prove individual supported probes on a nullable/floating unique index.

func TranslateCorrelation

func TranslateCorrelation(v values.Value, alias values.CorrelationIdentifier, replacement values.Value) values.Value

TranslateCorrelation replaces all QuantifiedObjectValue references matching `alias` with `replacement` in the given value tree. This is Go's equivalent of Java's:

TranslationMap.regularBuilder()
    .when(alias).then((src, leaf) -> replacement)
    .build()
value.translateCorrelations(translationMap, true)

Returns the transformed value (original if no match).

func TranslateCorrelations

func TranslateCorrelations(v values.Value, aliasMap map[values.CorrelationIdentifier]values.Value) values.Value

TranslateCorrelations replaces all QuantifiedObjectValue references matching any alias in the map with the corresponding replacement. Multiple-alias variant of TranslateCorrelation.

func TupleKeyUniquenessMatchesLogicalEquality

func TupleKeyUniquenessMatchesLogicalEquality(
	physicalTypes []values.Type,
	keyColumnCount int,
) bool

TupleKeyUniquenessMatchesLogicalEquality reports whether uniqueness of a raw FDB tuple key implies uniqueness under the engine's logical row-equality relation. The type vector must be authoritative and exactly aligned with the whole key. FLOAT/DOUBLE fail because FDB preserves distinct raw NaN sign/payload encodings while logical DISTINCT canonicalizes every NaN. (The logical row comparator does distinguish signed zero; predicate `=` is the operation that equates the zero signs.) Nullable components are safe here: a primary tuple key can contain only one raw nil value at a coordinate.

func ValueIdentityMatchesLogicalEquality

func ValueIdentityMatchesLogicalEquality(typ values.Type) bool

ValueIdentityMatchesLogicalEquality reports whether the runtime's lossless physical identity for a value agrees with SQL row equality. It is recursive because ARRAY/STRUCT group keys and DISTINCT rows can contain floating values below the top level. Unknown/ANY types fail closed: a visible literal or Go carrier cannot substitute for missing authoritative metadata.

Types

type BoundedCostHinter

type BoundedCostHinter interface {
	// HintCostWithin returns this expression's cost given its children's
	// rolled-up costs, the interval its own ProvenCardinalities derived, and
	// the active statistics provider.
	HintCostWithin(childCosts []Cost, bounds Cardinalities, stats StatisticsProvider) Cost
}

BoundedCostHinter is the optional interface an operator implements when a component of its Cost OTHER than Cardinality is a function of its own OUTPUT cardinality. Such a formula must consume the CLAMPED value, so it receives the proven interval and applies the clamp itself, before those terms are computed.

Clamping such a Cost afterwards leaves the rest of it computed from the disproven number. The pinned reproducer is the recursive level union: its hint charges buffer CPU proportional to estimated output, so a one-row seed with a zero-estimated recursive leg pays ZERO buffer work, and an after-the-fact clamp would then assert the output is at least one row — erasing the level-union-vs-DFS cost distinction the buffer term exists to draw, in the name of fixing it.

A Cost emitted through this path is already clamped, and ClampCost is idempotent, so a combine site may apply the shared clamp uniformly either way.

type Cardinalities

type Cardinalities struct {
	Min Cardinality
	Max Cardinality
}

Cardinalities captures both minimum and maximum cardinality bounds of an expression. Matches Java's CardinalitiesProperty.Cardinalities.

func AtMostOne

func AtMostOne() Cardinalities

AtMostOne returns Cardinalities{min=0, max=1}.

func ExactlyOne

func ExactlyOne() Cardinalities

ExactlyOne returns Cardinalities{min=1, max=1}.

func IntersectCardinalities

func IntersectCardinalities(items []Cardinalities) Cardinalities

IntersectCardinalities merges cardinalities for an intersection operation. For min: if both sides are known, set to 0 (intersection can be empty); if either is unknown, result is unknown. For max: take the min of known maxes (intersection bounded by smallest input); unknown propagates. Matches Java's CardinalitiesVisitor.intersectCardinalities().

func LimitBound

func LimitBound(limit int64, child Cardinalities) Cardinalities

LimitBound exposes limitBound to the plans layer so RecordQueryLimitPlan's method and the LogicalLimitExpression arm run the SAME arithmetic.

func ProvenCardinalitiesFrom

func ProvenCardinalitiesFrom(e expressions.RelationalExpression, child []Cardinalities) Cardinalities

ProvenCardinalitiesFrom derives the interval e proves from EXPLICIT child intervals, bypassing the walk entirely.

This is the method-level agreement point: it is the same dispatch provenCardinalities performs, so a test can assert that a plan's own ProvenCardinalities method and a logical twin's arm produce the identical interval FROM THE SAME CHILD INTERVALS. Asserting agreement on whole-walk outputs instead would be self-refuting — the property map weakens child bounds across all members while the concrete walk uses the concrete child, so their outputs legitimately differ. The invariant is that no one re-forks the per-operator derivation, not that every walk sees the same tree.

func ProvenCardinalitiesOf

func ProvenCardinalitiesOf(e expressions.RelationalExpression) Cardinalities

ProvenCardinalitiesOf derives the interval e proves, composing child bounds order-independently over all members of each child Reference.

Exposed for the walks that live outside this package (the concrete join-ordering cost and partition ranking) and for the tests that pin order-independence and logical/physical parity.

func UnionCardinalities

func UnionCardinalities(items []Cardinalities) Cardinalities

UnionCardinalities merges cardinalities for a union operation. Min and max are sums of known components; unknown propagates. Matches Java's CardinalitiesVisitor.unionCardinalities().

func UnknownCardinalities

func UnknownCardinalities() Cardinalities

UnknownCardinalities returns Cardinalities where both min and max are unknown.

func UnknownMaxCardinality

func UnknownMaxCardinality() Cardinalities

UnknownMaxCardinality returns Cardinalities{min=0, max=unknown}.

func WeakenCardinalities

func WeakenCardinalities(items []Cardinalities) Cardinalities

WeakenCardinalities merges cardinalities by taking the least constraining bounds. For min: take the lower of known mins (or unknown if either is unknown). For max: take the higher of known maxes (or unknown if either is unknown). Matches Java's CardinalitiesVisitor.weakenCardinalities().

func (Cardinalities) Equal

func (c Cardinalities) Equal(other Cardinalities) bool

Equal reports whether two Cardinalities are equal.

func (Cardinalities) Floor

func (c Cardinalities) Floor(minimum int64) Cardinalities

Floor returns new Cardinalities with both min and max floored at the given minimum. If neither bound changes, returns the receiver. Matches Java's Cardinalities.floor().

func (Cardinalities) GetMaxCardinality

func (c Cardinalities) GetMaxCardinality() Cardinality

GetMaxCardinality returns the maximum cardinality bound.

func (Cardinalities) GetMinCardinality

func (c Cardinalities) GetMinCardinality() Cardinality

GetMinCardinality returns the minimum cardinality bound.

func (Cardinalities) Times

func (c Cardinalities) Times(other Cardinalities) Cardinalities

Times multiplies both bounds with the other Cardinalities.

type Cardinality

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

Cardinality represents a single cardinality bound — either a known non-negative int64 or unknown. Matches Java's CardinalitiesProperty.Cardinality.

func MultiplyCardinality

func MultiplyCardinality(factor int64, bound Cardinality) Cardinality

MultiplyCardinality multiplies an exact non-negative factor by one cardinality bound without allowing int64 wraparound to reach OfCardinality. An unrepresentable product conservatively becomes unknown.

A negative factor is not a cardinality and cannot be scaled by one; it yields unknown rather than a wrapped or panicking product.

func OfCardinality

func OfCardinality(v int64) Cardinality

OfCardinality returns a known Cardinality with the given value. Panics if v < 0, matching Java's Preconditions.checkArgument.

func ProvenFullEqualityMultiplicity

func ProvenFullEqualityMultiplicity(
	comps []*predicates.ComparisonRange,
	physicalTypes []values.Type,
	keyColumnCount int,
	semantics KeyUniquenessSemantics,
) Cardinality

ProvenFullEqualityMultiplicity returns the finite physical-row upper bound established by a complete equality bind under the stated uniqueness semantics. It is the shared cardinality authority for primary-key and secondary-UNIQUE probes; ordering deliberately continues to use PhysicalEqualityShape because one NULL prefix is physically fixed even when it is not unique.

For a NULLS DISTINCT secondary index, ordinary `= NULL` remains safely empty. IS NULL, static `IS NOT DISTINCT FROM NULL`, and a nullable dynamic null-safe equality decline when the physical key can contain NULL. A NOT NULL target makes the static NULL-only forms empty and keeps a dynamic null-safe equality safe: NULL yields no rows, while every non-NULL value is uniqueness-checked.

func UnknownCardinality

func UnknownCardinality() Cardinality

UnknownCardinality returns an unknown Cardinality.

func (Cardinality) Equal

func (c Cardinality) Equal(other Cardinality) bool

Equal reports whether two cardinalities are equal.

func (Cardinality) Floor

func (c Cardinality) Floor(minimum int64) Cardinality

Floor returns max(value, minimum). If unknown, returns the receiver unchanged. If the current value >= minimum, returns the receiver unchanged. Matches Java's Cardinality.floor().

func (Cardinality) IsUnknown

func (c Cardinality) IsUnknown() bool

IsUnknown reports whether this cardinality is unknown.

func (Cardinality) Times

func (c Cardinality) Times(other Cardinality) Cardinality

Times multiplies two cardinalities. If either is unknown, the result is unknown. Matches Java's Cardinality.times().

An UNREPRESENTABLE product conservatively becomes unknown rather than wrapping. Java's Cardinality.times() multiplies raw and hands the result to ofCardinality's non-negative Preconditions check, so an overflowing product throws there; the Go port inherited that shape and would panic inside OfCardinality — in library code, on a bound the planner derives while costing (`LIMIT 4000000000` joined against itself reaches it through the join arm). Unknown is the sound weakening: it only ever LOOSENS a bound, so no clamp built on it can floor or cap past what is actually proved.

func (Cardinality) Value

func (c Cardinality) Value() int64

Value returns the cardinality value. Panics if unknown, matching Java's OptionalLong.getAsLong() semantics.

type CardinalityProver

type CardinalityProver interface {
	ProvenCardinalities(child []Cardinalities) Cardinalities
}

CardinalityProver is the interface a physical plan implements to state the structural [min, max] interval it PROVES for its own output, given the intervals proved for its children.

Defined here, implemented in the `plans` package beside each operator's cost formula — the exact CostHinter precedent. Keeping the proof next to the formula it constrains is the point: the six estimates RFC-195 corrects each accumulated because "this operator guarantees a row" lived in a different file from "this operator costs in*0.7", and nothing made the two agree.

child carries one interval per child, in the operator's own child order. Implementations MUST tolerate a child slice of unexpected length (an unresolved or partially built edge) by returning UnknownCardinalities rather than indexing past it — an absent child is an absent proof, never a bound.

type CollectedStatistics

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

CollectedStatistics is RFC-236's statistics provider: per-record-type row counts gathered by the offline collector.

It is deliberately NOT MapStatistics. Two differences, both load-bearing.

COMPLETENESS. It is constructed only when every record type in the schema has a fresh entry, so a named lookup can never miss. MapStatistics is built from whatever was available and answers a miss with LeafScanCardinality; that is fine for its callers and fatal here, because a miss is not merely wrong, it is wrong in the INVERTING direction. LeafScanCardinality is 1e6 — larger than almost any real count — so one missing type standing beside a real 150-row count makes the missing one the largest table in the schema and drives the join from the wrong side. Statistics that do that are worse than none, which at least ties.

This inversion is reachable under ONE provider, which is what makes it real: MapStatistics' fallback is a CONSTANT, so the miss and the hit are drawn from the same map and still differ by four orders of magnitude. It is a different mechanism from the empty-NAME case below, and the two are easy to conflate — an earlier revision of the FullUnorderedScan comment described a universal-vs-typed comparison as inverting when, under one provider, it is not. Keep them apart: a MISSING name inverts because the fallback is a constant; the EMPTY name would invert only for a store larger than LeafScanCardinality.

THE EMPTY NAME IS NOT A RECORD TYPE. Production sites ask for it when a leaf's record types are unknown — a nil plan, or a scan carrying no type list (planning_cost_model.go, plans/cost.go, and FullUnorderedScanExpression with an empty type list). The population is deliberately NOT written as a number here: an earlier revision said "four", RFC-236 then added the fifth, and the stale count sat in the file that states the rule about comments outrunning code. To see the current set:

grep -rn --include='*.go' 'RecordTypeCardinality("")' . |
  grep -v _test.go | grep -v '^[^:]*:[0-9]*:\s*//'

Semantically that leaf could read anything in the store, so the honest answer is the whole store: the SUM of every type. That keeps the value on the same scale as the data, so comparing it against a real count still means something, whatever the store's size. A magic constant does not.

func NewCollectedStatistics

func NewCollectedStatistics(perType map[string]float64) CollectedStatistics

NewCollectedStatistics builds a provider over a COMPLETE per-type map. The caller is responsible for completeness (RFC-236 §5.2); this type cannot check it, because it does not know the schema.

func (CollectedStatistics) RecordTypeCardinality

func (s CollectedStatistics) RecordTypeCardinality(name string) float64

RecordTypeCardinality returns the collected count for name.

For the EMPTY name — an unknown-type leaf — it returns the whole store. For a name that is not in the map at all it also returns the whole store rather than LeafScanCardinality: an over-estimate on the store's own scale is the safe direction, where the 1e6 default is an over-estimate on nobody's scale.

type CoordinateBoundClaim

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

CoordinateBoundClaim is an assertion about an ordering — "the rows flowed are distinct", or "these coordinates are the COMPLETE storage key" — BOUND to the coordinate set it was proved over.

The binding is the whole point. Neither is a property an ordering carries in the abstract: each is a property of a specific set of coordinates. An ordering by (a, b, c) is also an ordering by (a, b); the first may be strict, the second need not be. Java models this as a bare boolean and documents the resulting hazard in the field's own javadoc (Ordering.java:185-192), which ends with a hand-written interpretation rule a reader is expected to know and apply:

"I think for now we should just interpret this indicator in a way that only
 enumerated orderings that contain all values of the ordering are strict if
 this indicator is true."

That rule is what this type enforces structurally instead. A claim records the coordinates it was computed over, and IsDistinct answers false the moment any of them is missing from the ordering asking the question. So the three legal outcomes of a reduction — drop the claim, recompute it, or keep it only when nothing was actually dropped — are the only outcomes reachable, and "carry it forward unexamined" is not expressible.

A caller that has genuinely PROVED distinctness over the keys it is about to construct mints a fresh claim with DistinctOverAllKeys. A caller that is merely propagating someone else's claim passes the source ordering's carried claim, which stays bound to the source's coordinates and therefore fails closed under any reduction or rename it does not survive.

func ClaimOverAllKeysIf

func ClaimOverAllKeysIf(holds bool) CoordinateBoundClaim

ClaimOverAllKeysIf is the property-neutral guarded producer constructor. The same binding serves the STORAGE-COMPLETENESS claim — "these coordinates are the whole storage key" — a different assertion about the same object that fails under reduction for the same reason.

func DistinctOverAllKeys

func DistinctOverAllKeys() CoordinateBoundClaim

DistinctOverAllKeys mints a FRESH claim covering exactly the key set of the ordering being constructed. It is the producer's API: use it only where the distinctness of those specific coordinates was just established.

func DistinctOverAllKeysIf

func DistinctOverAllKeysIf(distinct bool) CoordinateBoundClaim

DistinctOverAllKeysIf is DistinctOverAllKeys guarded by a producer-side fact.

func IntersectClaims

func IntersectClaims(a, b CoordinateBoundClaim) CoordinateBoundClaim

IntersectClaims is the claim for a derivation whose distinctness needs BOTH inputs to be distinct — a union merge, in Java's terms `leftOrdering.isDistinct() && rightOrdering.isDistinct()`. The result is bound to the UNION of both coordinate sets, because both proofs have to survive into the merged ordering for the conjunction to mean anything.

func NotDistinct

func NotDistinct() CoordinateBoundClaim

NotDistinct is the absence of a claim.

func (CoordinateBoundClaim) IsClaimed

func (c CoordinateBoundClaim) IsClaimed() bool

IsClaimed reports whether a distinctness assertion was ever made. It says nothing about whether the assertion still holds — that is holdsOver's question, and IsDistinct is the only public answer.

type Cost

type Cost struct {
	// Cardinality is the estimated number of rows this expression
	// emits when executed.
	Cardinality float64

	// CPU is the estimated row-equivalent CPU work to produce those
	// rows, summed over the sub-tree (children's CPU is rolled up
	// into parents).
	CPU float64
}

Cost is a Go-native heuristic cost: a cardinality estimate plus a CPU estimate. Lower is cheaper.

Both axes are in "logical row" units so Total = Cardinality + CPU is well-formed. The heuristic does NOT model wall-clock time and does NOT match Java's cost model bit-for-bit — per RFC-024 we're free to pick a Go-native shape.

func BestMemberCostWith

func BestMemberCostWith(e expressions.RelationalExpression, stats StatisticsProvider) Cost

BestMemberCostWith costs expression e with FULLY RECURSIVE best-member sub-products: every child Reference (transitively) is costed at its cheapest exploratory or final member, not its first. This is Cascades' "combined cost with inputs" (§3.1). It is kept separate from EstimateCostWith so winner extraction and stage advancement retain their established exploratory-first behaviour; current join ordering uses its concrete-plan cost walk instead.

Per-call memoisation keeps a shared (e.g. union-find-merged) sub-product to one walk: O(N+K) not O(N*K). The visited recursion- stack guard is defensive — the child-Reference DAG is acyclic (memo_merge.go reachable/mergeable forbids cycle-creating merges).

Pass nil to use DefaultStatistics.

func BestRefCost

func BestRefCost(ref *expressions.Reference) Cost

BestRefCost returns the cheapest member's cost in `ref` using DefaultStatistics. See BestRefCostWith for the stats-bound variant.

Returns the zero Cost if `ref` is nil or empty.

func BestRefCostWith

func BestRefCostWith(ref *expressions.Reference, stats StatisticsProvider) Cost

BestRefCostWith returns the cheapest member's cost in `ref` under the given StatisticsProvider. All exploratory and final members of the TOP Reference are ranked by best; their children recurse via first-member (see EstimateCostWith). This helper backs BestRefCardinality and cost-sanity checks; production Reference.GetBest callers use CostLessWith directly.

Memoisation: builds a per-call `map[*Reference]Cost` so child References shared across multiple members are walked only once.

Pass nil for stats to use DefaultStatistics.

func ClampCost

func ClampCost(c Cost, bounds Cardinalities) Cost

ClampCost returns c with its Cardinality constrained to the proven interval. The CPU axis is untouched: every formula in this package derives CPU from the rows it CONSUMES (already-clamped child cardinalities) rather than from the rows it emits, so a clamp on the output leaves those terms consistent.

The one operator whose CPU genuinely derives from its OWN OUTPUT cardinality implements BoundedCostHinter instead and clamps before computing that term — see that interface's doc comment for why applying the clamp afterwards would erase the very cost distinction the formula exists to draw.

func CostWithinBounds

func CostWithinBounds(
	e any,
	child []Cost,
	bounds Cardinalities,
	stats StatisticsProvider,
	unbounded func() Cost,
) Cost

CostWithinBounds is the ONE dispatch every cost walk's combine step uses: route through BoundedCostHinter when the operator has an output-derived CPU term, otherwise run `unbounded` — and clamp either way.

One helper rather than the same four-line type-assert repeated at each combine site. Repeating it is how the sites drift: three of them would clamp and the fourth would quietly not, which is the shape of the original defect (a rule applied at some walks and not the one that elects plans) reproduced one level down. ClampCost is idempotent, so applying it to an already-clamped bounded result is a deliberate no-op rather than a special case.

func DistinctCost

func DistinctCost(child Cost) Cost

DistinctCost: duplicate elimination over the child stream.

func EstimateCost

func EstimateCost(e expressions.RelationalExpression) Cost

EstimateCost walks the expression tree rooted at e and returns the aggregated Cost estimate using DefaultStatistics. Equivalent to EstimateCostWith(e, DefaultStatistics{}).

Sub-Reference recursion picks the first member's cost (see package doc for rationale).

Returns the zero Cost on nil input.

Cost is rolled UP from children: a parent's CPU includes each child's CPU plus the parent's own work. Cardinality is the parent operator's own emit count (NOT a sum of children unless the operator produces a union of child rows).

func EstimateCostWith

func EstimateCostWith(e expressions.RelationalExpression, stats StatisticsProvider) Cost

EstimateCostWith is EstimateCost driven by the provided StatisticsProvider. Scan operators query the provider for per-record-type cardinality; all other operators use the default per-operator constants.

Sub-Reference recursion picks the FIRST exploratory member's cost, falling back to the first final member only for a finals-only Reference (see package doc). This is the cost used by GetBest / winner extraction / stage advancement; keeping it exploratory-first preserves the established winner-selection behaviour. BestMemberCostWith remains available to callers that explicitly need a fully recursive best-member walk.

Pass nil to use DefaultStatistics.

func FetchCost

func FetchCost(child Cost) Cost

FetchCost: primary-key fetch of the full record per index entry.

func FilterCost

func FilterCost(child Cost, numPreds int) Cost

FilterCost: one selectivity factor per predicate (min one).

func FirstOrDefaultCost

func FirstOrDefaultCost(child Cost) Cost

FirstOrDefaultCost: at most one row out, the child's work paid in full.

func FlatMapCost

func FlatMapCost(outer, inner Cost) Cost

FlatMapCost: a correlated dependent join re-runs the inner once per outer row.

The CPU has two per-outer-row terms: outerCard*innerCPU (the inner's own work, re-paid each iteration) PLUS outerCard*IterationOverhead — a fixed per-iteration re-execution overhead (open the inner cursor, init the range read, bind the correlation). The overhead term is what makes driving the nested loop from the SMALLER side cheaper when criterion #2 abstains (two full-scan drivers, e.g. a 20-row vs a 200-row outer in a re-enumerated multi-way sub-join): the 200-row driver pays 10x the per-iteration setup. It is a Go-only read-side cost extension that lives ONLY here (the compareJoinOrdering concrete-cost path), sized as a tie-breaker so it never flips a clear cardinality/total-cost winner (RFC-041/042).

Cardinality is outerCard*innerCard, with NO extra FilterSelectivity factor — the join predicate is NOT reapplied here because it is already accounted for inside inner.Cardinality. Every FlatMap inner reaches this formula having already applied the join predicate exactly once, in whichever form the inner subtree takes: an equality-bound SARG on the leaf scan/index (scanLikeCost/BoundSelectivity already discounts by the bind), a PredicatesFilter the implementation rule stacked on the inner for a residual join predicate (FilterCost already applies FilterSelectivity), or a FirstOrDefault collapse for a scalar/EXISTS inner (FirstOrDefaultCost floors cardinality at ~1, the correct one-row-per-outer count for that shape — not outerCard*sel, which under-counts a guaranteed-one-row-out wrapper). So inner.Cardinality already IS the true rows-yielded-per-outer-row count; using it directly gives the same total cardinality a NestedLoopJoinCost over the SAME logical join computes (outerCard*innerCardRaw*FilterSelectivity), because innerCard here equals innerCardRaw*FilterSelectivity — each shape just applies the predicate at a different place in its own subtree. Reapplying FilterSelectivity on top of an already-filtered innerCard would double-count the predicate and systematically undercost FlatMap relative to the materialized NestedLoopJoin over the identical join (see NestedLoopJoinCost).

outerCard/innerCard are NOT zero-guarded. A zero Cardinality here is never "the child's cost wasn't computed" — every leaf/operator formula in this file that lacks a real cardinality either floors it at >=1 (FirstOrDefault, AggregateIndex, Explode) or returns before reaching FlatMapCost at all (the `len(child) < 2` guards in the callers). It IS the genuine output of an empty-relation operator — RecordQueryLimitPlan.HintCost's explicit cardinality-0 for LIMIT 0, or RecordQueryInUnionPlan.HintCost's cost-0 for a zero-length literal IN source (plan/plans/cost.go) — and must propagate unchanged: `SELECT * FROM t1, (SELECT * FROM t2 LIMIT 0)` joins to exactly zero rows, not outerCard*LeafScanCardinality. Substituting LeafScanCardinality for an exact zero silently turned a free empty join into the single most expensive shape on the plan, which can flip the winner away from it.

func InMemorySortCost

func InMemorySortCost(child Cost) Cost

InMemorySortCost: materialize + O(n log n). Note: NO physical-wrapper discount — an in-memory sort must stay strictly more expensive than index-based elimination.

The returned Cardinality is the child's EXACT count, never clamped — the same zero-preservation reasoning as FlatMapCost/NestedLoopJoinCost applies (a LIMIT-0 child sorts zero rows, and must report zero, not a phantom one). Only the LOG argument is floored at >=1: math.Log2 is undefined below that, and the floor is purely a numerical-domain guard local to logN — it must never leak into the Cardinality the caller rolls up.

func IntersectionCost

func IntersectionCost(child []Cost) Cost

IntersectionCost: output bounded by the smallest child; work ~ scanning every child + per-output comparison-key merge. Carries the physical-wrapper CPU discount like the other join-tree operators so the plan HintCost and the concrete join-ordering cost agree exactly.

func MapCost

func MapCost(child Cost) Cost

MapCost: per-row projection, cardinality-preserving.

func NestedLoopJoinCost

func NestedLoopJoinCost(
	outer, inner Cost,
	numPreds int,
	uniqueKeyConjuncts int,
	preservesOuter bool,
) Cost

NestedLoopJoinCost: MATERIALIZED nested-loop join, outer × inner with per-pair filter.

The inner subtree is executed ONCE: the executor (executor.go executeNestedLoopJoin) materializes the inner into a buffer via CollectAllBounded, then iterates the buffered rows per outer row. So the inner's own work (inner.CPU) is paid ONCE — NOT outerCard times. The per-pair term outerCard*innerCard*FilterCPU models iterating the buffer and evaluating the join predicate for every (outer,inner) pair (the in-memory work that DOES scale with the product).

This materialization is what distinguishes the NLJ from a correlated FlatMap: FlatMapCost charges outerCard*innerCPU because the FlatMap RE-EXECUTES (re-scans from FDB) its inner once per outer row, whereas the materialized NLJ scans the inner once and re-iterates the buffer. Charging the NLJ outerCard*inner.CPU (as if it re-scanned) erased that distinction and let a re-scan FlatMap tie/beat the materialized NLJ for a NON-PROBE inner — the RFC-152 preserved-only LEFT-OUTER regression. A card-1 PROBE inner keeps the FlatMap cheapest regardless (its outerCard*~1 work beats materialize+iterate).

numPreds is the join's predicate count, compounded exactly like FilterCost compounds numPreds selectivity factors for PredicatesFilter — one FilterSelectivity factor PER predicate, not one factor total. A 2-predicate FlatMap's inner is a PredicatesFilter that already compounds FilterSelectivity^2; before this parameter existed, NestedLoopJoinCost always applied exactly ONE factor regardless of predicate count, so a multi-predicate join's SAME logical cardinality was estimated two different ways by the two physical shapes — the identical group-property violation PhysicalWrapperCostMultiplier caused, one level removed: here it is the join's OWN selectivity term, not an external per-node discount, that failed to scale with an input (predicate count) both shapes must agree on.

outer.Cardinality/inner.Cardinality are NOT zero-guarded, for the same reason FlatMapCost's are not (see its doc comment): an exact zero here is a genuine empty child (LIMIT 0, an empty literal IN-list), never "unknown", and must produce a genuine zero-row join rather than being inflated to outerCard*LeafScanCardinality.

uniqueKeyConjuncts is how many of numPreds conjuncts the caller has PROVEN (plans/cost.go's nestedLoopJoinUniqueKeyConjuncts) to be a full equality bind on the inner leg's own UNIQUE key (primary key or a declared-UNIQUE index), with each logical equality class proved to contain at most one raw key. Signed-zero/NaN float classes, precision-cliff integer classes, nullable NULL binds, and unknown types fail closed before this formula. For accepted conjuncts the flat per-predicate FilterSelectivity guess is provably wrong: the equality can match AT MOST ONE inner row per pair, so their TRUE combined selectivity is exactly 1/innerCard, not FilterSelectivity^uniqueKeyConjuncts. This is the exact invariant FlatMapCost's doc comment requires of this function — the two physical shapes of the SAME logical unique-key equality join must compute the SAME cardinality (outerCard), and only 1/innerCard achieves that here; the flat guess overestimates a 1000-row inner's join by ~500x (0.5 vs 1/1000). The remaining (numPreds-uniqueKeyConjuncts) conjuncts still take the flat FilterSelectivity fallback — the unique-key bind explains only ITS OWN conjuncts' selectivity, not any other residual predicate's.

uniqueKeyConjuncts<=0, or an innerCard<=0 the ratio cannot divide by, reproduces today's unconditional flat-selectivity formula exactly — the correction is a gated ADDITION, never a replacement of the honest fallback when uniqueness cannot be proven (e.g. a non-unique secondary-index equality join, where the flat guess remains the right answer).

func TypeFilterCost

func TypeFilterCost(child Cost) Cost

TypeFilterCost: record-type discrimination over the child stream.

func (Cost) Less

func (c Cost) Less(other Cost) bool

Less reports whether c is strictly cheaper than other under the current cost model. Tie-break on cardinality alone (smaller wins on equal Total) so a deterministic ordering exists for fuzz-pinned cost monotonicity.

func (Cost) Total

func (c Cost) Total() float64

Total returns the comparator-friendly total cost as Cardinality + CPU.

type CostHinter

type CostHinter interface {
	// HintCost returns this expression's cost given its children's
	// rolled-up costs and the active statistics provider.
	// Implementations should aggregate sumCPU = sum(child.CPU) +
	// own-CPU and compute cardinality based on the operator's
	// selectivity / row-shape contract. Use stats for table-level
	// cardinality when available (scan/index wrappers); most
	// operators ignore stats and derive cardinality from children.
	HintCost(childCosts []Cost, stats StatisticsProvider) Cost
}

CostHinter is the optional interface a RelationalExpression implements to override the cost model's default-arm pessimistic estimate. Used by opaque wrappers (e.g. cascades- internal physical-plan adapters) to declare a more accurate cost — typically equal to or lower than the corresponding logical operator's cost so cost-driven extraction prefers the physical plan.

type DefaultStatistics

type DefaultStatistics struct{}

DefaultStatistics returns LeafScanCardinality for every record type. Use this as a baseline when no real statistics are available.

func (DefaultStatistics) RecordTypeCardinality

func (DefaultStatistics) RecordTypeCardinality(_ string) float64

RecordTypeCardinality always returns LeafScanCardinality.

type Derivations

type Derivations struct {
	ResultValues []values.Value
	LocalValues  []values.Value
}

Derivations captures the result and local value derivations collected by the evaluator. Mirrors Java's DerivationsProperty.Derivations inner class.

  • ResultValues: the value trees describing what this plan node produces as output rows. After inlining child correlations, these are fully decorrelated.
  • LocalValues: value trees that appear locally in this node (predicates, comparison keys, etc.) but are not part of the result. Also fully decorrelated after inlining.

func DerivationsFromQuantifier

func DerivationsFromQuantifier(q expressions.Quantifier) *Derivations

DerivationsFromQuantifier is the exported version of derivationsFromQuantifier for use by wrapper implementations.

func DerivationsFromSingleChild

func DerivationsFromSingleChild(expr expressions.RelationalExpression) *Derivations

DerivationsFromSingleChild evaluates derivations for a single-child expression by recursing into its only quantifier. Exported for use by physical plan wrapper implementations. Mirrors Java's DerivationsVisitor.derivationsFromSingleChild.

func EmptyDerivations

func EmptyDerivations() *Derivations

EmptyDerivations returns the singleton empty Derivations.

func EvaluateDerivations

func EvaluateDerivations(expr expressions.RelationalExpression) *Derivations

EvaluateDerivations walks the expression tree bottom-up and computes the Derivations for the given expression. Mirrors Java's DerivationsProperty.evaluate / DerivationsVisitor.

The evaluator dispatches on the concrete physicalPlanExpression wrapper type (via the DerivationsEvaluable interface) or falls back to generic single-child / multi-child propagation.

type DerivationsEvaluable

type DerivationsEvaluable interface {
	EvaluateDerivations() *Derivations
}

DerivationsEvaluable is the optional interface physical-plan expression wrappers implement to compute their derivations. Mirrors the per-plan-type visit methods in Java's DerivationsVisitor.

type Distinctness

type Distinctness int

Distinctness specifies whether ordered records should be distinct.

const (
	DistinctnessDistinct Distinctness = iota
	DistinctnessNotDistinct
	DistinctnessPreserveDistinctness
)

type DynamicTypeProvider

type DynamicTypeProvider interface {
	GetDynamicTypes() []values.Type
}

DynamicTypeProvider is the optional interface an expression implements to expose its dynamic types. Matches Java's RelationalExpression.getDynamicTypes().

type ExpressionProperty

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

ExpressionProperty is a type-safe key for plan property maps. Instances are singletons compared by pointer identity — the same pattern Java's ExpressionProperty uses.

func (*ExpressionProperty) String

func (p *ExpressionProperty) String() string

type FixedStatistics

type FixedStatistics struct {
	Cardinality float64
}

FixedStatistics returns a fixed cardinality for every record type. Useful in tests that want a non-default scan cost.

func (FixedStatistics) RecordTypeCardinality

func (s FixedStatistics) RecordTypeCardinality(_ string) float64

RecordTypeCardinality returns the configured fixed value.

type IntersectionExpression

type IntersectionExpression interface {
	IsIntersection()
}

IntersectionExpression marks expressions whose children's comparison sets are INTERSECTED rather than unioned — the Go analogue of ComparisonsProperty's visitRecordQueryIntersectionOnKeyExpressionPlan / visitRecordQueryIntersectionOnValuesPlan overrides. A marker rather than a type switch over the concrete intersection plans, so a new intersection plan type joins the fold by implementing it rather than by being remembered.

The walk consuming both interfaces is the cost model's collectSargedComparisons. It lives there and not here because it descends a Reference to its PHYSICAL member, a notion belonging to the cascades package, which imports this one. There is exactly one such walk, matching Java's single comparisons() property.

type KeyUniquenessSemantics

type KeyUniquenessSemantics uint8

KeyUniquenessSemantics states which storage invariant a full equality bind may rely on. Primary keys are unique over the complete packed tuple. Secondary UNIQUE indexes use SQL's default NULLS DISTINCT semantics: the uniqueness check is skipped when any indexed component is NULL, so one NULL prefix can have arbitrarily many entries distinguished by their appended PK.

const (
	TupleKeyUnique KeyUniquenessSemantics = iota
	SecondaryUniqueNullsDistinct
)

type MapStatistics

type MapStatistics struct {
	// PerType maps record-type name → estimated cardinality.
	PerType map[string]float64
	// Fallback returned for names not present in PerType. ≤ 0
	// means "no fallback configured" — the package default
	// (LeafScanCardinality) is substituted.
	Fallback float64
}

MapStatistics returns per-record-type cardinalities from a map, falling back to a default for unknown names.

Fallback semantics: zero or negative means "use LeafScanCardinality (the package default)"; any positive value is used as-is. A caller who wants 0 as the actual fallback should use FixedStatistics{0} instead — MapStatistics treats 0 as "no fallback configured" to avoid silent zero-cardinality bugs (a zero scan cardinality would make every plan score zero and break cost-driven extraction).

func (MapStatistics) RecordTypeCardinality

func (s MapStatistics) RecordTypeCardinality(name string) float64

RecordTypeCardinality returns PerType[name] if present and > 0, otherwise Fallback when > 0, otherwise LeafScanCardinality. Zero counts are clamped to 1 to avoid zero-cost plans that break cost-driven plan selection.

type Ordering

type Ordering struct {
	// IsKnown is true when the expression's output order is guaranteed.
	IsKnown bool
	// Keys are the ordering key Values (in priority order: rows are
	// sorted by Keys[0] first, then Keys[1], etc.). Empty when
	// IsKnown=false.
	Keys []values.Value
	// Descending is parallel to Keys. true = descending order for that
	// key. nil or shorter than Keys means all ascending.
	Descending []bool
	// NullsFirst is parallel to Keys. true = NULLs sort before non-NULLs
	// for that key. nil or shorter than Keys means the FDB-natural default
	// per key (ASC → nulls first, DESC → nulls last) — see NullsFirstAt.
	// Only "counterflow" producers (an explicit ORDER BY ... NULLS clause
	// that inverts the natural order, materialized by an in-memory sort)
	// need to populate this; natural-order producers (index scans) leave it
	// empty and NullsFirstAt derives the natural placement. Carrying it lets
	// a parent sort correctly decline to elide against a counterflow stream
	// (e.g. an inner `ASC NULLS LAST` does NOT satisfy an outer `ASC`).
	NullsFirst []bool
}

Ordering captures the deterministic-output-order property of a RelationalExpression. An IsKnown=true Ordering means the producer guarantees rows in Keys order; IsKnown=false means no order guarantee (typical for Union / FullScan / hash-distinct).

func EstimateOrdering

func EstimateOrdering(e expressions.RelationalExpression) Ordering

EstimateOrdering returns the static ordering guarantee for an expression. Mirrors Java's OrderingProperty walk but in a much reduced form.

Per-operator semantics:

FullUnorderedScan: IsKnown=false (FDB scan order is by primary
    key, but the LOGICAL unordered-scan expression is treated as
    truly unordered; ordered access is modeled by the physical
    ordered-scan wrappers, which advertise their ordering via
    OrderingHinter).
Sort: IsKnown=true, Keys = the sort keys.
Filter / Projection / TypeFilter: inherits child ordering (these
    operators preserve row order).
Distinct / Unique: inherits child ordering (dedup drops repeats
    without reordering survivors — see the case arms below).
Union / Intersection: IsKnown=false (logical concatenation loses
    order; ordered physical set operations advertise theirs via
    OrderingHinter).
Insert / Update / Delete: inherits inner ordering (DML is
    pass-through).

Returns the zero-value Ordering ({IsKnown=false, Keys=nil}) when the expression's ordering can't be determined.

func (Ordering) DescendingAt

func (o Ordering) DescendingAt(i int) bool

DescendingAt reports whether key i is descending (false past the slice).

func (Ordering) NullsFirstAt

func (o Ordering) NullsFirstAt(i int) bool

NullsFirstAt reports the NULL placement for key i, defaulting to the FDB-natural order (ASC → nulls first, DESC → nulls last) when NullsFirst is unset for that key.

type OrderingBinding

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

OrderingBinding represents a binding in the ordering: either a fixed comparison binding or a directional sort binding. Mirrors Java's Ordering.Binding.

func ChooseBinding

func ChooseBinding() OrderingBinding

ChooseBinding creates a binding where the sort order will be chosen during enumeration.

func FixedBinding

func FixedBinding(comparison any) OrderingBinding

FixedBinding creates a fixed (equality-bound) ordering binding.

func SingleFixedBinding

func SingleFixedBinding(bindings []OrderingBinding) OrderingBinding

FixedBinding returns the single fixed binding from the list. Panics if there isn't exactly one.

func SortedBinding

func SortedBinding(sortOrder ProvidedSortOrder) OrderingBinding

SortedBinding creates a sorted ordering binding.

func (OrderingBinding) GetComparison

func (b OrderingBinding) GetComparison() any

func (OrderingBinding) GetSortOrder

func (b OrderingBinding) GetSortOrder() ProvidedSortOrder

func (OrderingBinding) IsChoose

func (b OrderingBinding) IsChoose() bool

func (OrderingBinding) IsFixed

func (b OrderingBinding) IsFixed() bool

func (OrderingBinding) IsSorted

func (b OrderingBinding) IsSorted() bool

type OrderingBindingKind

type OrderingBindingKind int
const (
	OrderingBindingSorted OrderingBindingKind = iota
	OrderingBindingFixed
	OrderingBindingChoose
)

type OrderingHinter

type OrderingHinter interface {
	HintOrdering() Ordering
}

OrderingHinter is the optional interface a RelationalExpression implements to advertise its output ordering. Used by physical wrappers (e.g. index scan) to declare that their output is ordered by specific keys without the ordering property needing to know every concrete wrapper type.

type OrderingMergeKind

type OrderingMergeKind int

OrderingMergeKind determines the semantics of merging two orderings.

const (
	OrderingMergeUnion OrderingMergeKind = iota
	OrderingMergeIntersection
)

type PhysicalEqualityComponentShape

type PhysicalEqualityComponentShape struct {
	LogicalEquality     bool
	MayFanOut           bool
	PhysicallyFixed     bool
	UnsupportedKnownNaN bool
	Unsatisfiable       bool

	// SuccessfulSeekUpperBound is the maximum number of physical ranges this
	// component opens for a non-NaN execution. It is one or two. A terminal
	// signed-zero equality can still be non-fixed while needing only one seek,
	// because the adjacent zero subtrees can be covered by one inclusive range.
	SuccessfulSeekUpperBound int64

	// ProvenRowMultiplicity is the maximum number of distinct physical keys a
	// UNIQUE/primary-key equality can match. Dynamic FLOAT/DOUBLE and genuinely
	// Unknown values deliberately remain unknown because their domain includes
	// the unsupported NaN equivalence class.
	ProvenRowMultiplicity Cardinality
}

PhysicalEqualityComponentShape describes how one logical equality binds an FDB tuple-key component. In particular, PhysicallyFixed is deliberately distinct from ComparisonRange.IsEquality: IEEE equality binds both tuple encodings of signed zero for a FLOAT or DOUBLE key.

func PhysicalEqualityShapeForComponent

func PhysicalEqualityShapeForComponent(
	cr *predicates.ComparisonRange,
	physicalType values.Type,
	terminalWidening bool,
) PhysicalEqualityComponentShape

PhysicalEqualityShapeForComponent classifies one ComparisonRange. The terminalWidening flag affects seek count only; a range spanning both signed zeros is still not physically fixed and cannot justify dropping that key from an ordering.

type PhysicalEqualityShape

type PhysicalEqualityShape struct {
	Components           []PhysicalEqualityComponentShape
	EqualityPrefixLength int
	MayFanOut            bool
	PhysicallyFixed      bool
	UnsupportedKnownNaN  bool
	Unsatisfiable        bool

	// SuccessfulSeekUpperBound saturates at math.MaxInt64 when its exact
	// product is not representable. SuccessfulSeekUpperBoundExact distinguishes
	// that conservative cost value from an exact execution-count proof.
	SuccessfulSeekUpperBound      int64
	SuccessfulSeekUpperBoundExact bool

	// ProvenRowMultiplicity is unknown if any component cannot prove a finite
	// physical multiplicity or if checked multiplication overflows.
	ProvenRowMultiplicity Cardinality
}

PhysicalEqualityShape is the checked product of the leading equality components in a comparison prefix. Components is aligned with the consumed leading equalities; the first non-equality ends the shape.

func PhysicalEqualityShapeForComparisons

func PhysicalEqualityShapeForComparisons(
	comps []*predicates.ComparisonRange,
	physicalTypes []values.Type,
	allowTerminalWidening bool,
) PhysicalEqualityShape

PhysicalEqualityShapeForComparisons classifies the contiguous leading equality prefix using authoritative physical key types. A known physical type wins over the RHS declaration; Unknown stays unknown. The comparand's declaration or Go representation cannot resolve missing metadata or a physical-width disagreement in a shared index. This is the one planner-side authority shared by ordering, cardinality, and cost.

allowTerminalWidening is true for ordinary tuple scans, whose final signed zero can use one inclusive [-0,+0] range, and false for vector partitions, where each sign names a distinct graph that must be opened independently.

type PredicateCountByLevelInfo

type PredicateCountByLevelInfo struct {
	// LevelToPredicateCount maps level number to predicate count.
	LevelToPredicateCount map[int]int
}

PredicateCountByLevelInfo holds the number of predicates at each level of a RelationalExpression tree. Level 0 is the leaf level; higher levels are closer to the root. Matches Java's PredicateCountByLevelProperty.PredicateCountByLevelInfo.

func EvaluatePredicateCountByLevel

func EvaluatePredicateCountByLevel(expr expressions.RelationalExpression) PredicateCountByLevelInfo

EvaluatePredicateCountByLevel walks the expression tree and counts predicates at each level. Level 0 corresponds to leaves; the root gets the highest level number. Matches Java's PredicateCountByLevelProperty.evaluate.

func (PredicateCountByLevelInfo) GetHighestLevel

func (info PredicateCountByLevelInfo) GetHighestLevel() int

GetHighestLevel returns the maximum level recorded, or -1 if empty.

type PropertyMap

type PropertyMap map[*ExpressionProperty]any

PropertyMap holds computed property values for a single plan.

func (PropertyMap) GetBool

func (m PropertyMap) GetBool(p *ExpressionProperty) bool

GetBool returns the bool value for the given property, or false if absent.

func (PropertyMap) GetCardinalities

func (m PropertyMap) GetCardinalities() Cardinalities

GetCardinalities returns the Cardinalities value for PropCardinalities, or UnknownCardinalities() if absent.

func (PropertyMap) GetDerivations

func (m PropertyMap) GetDerivations() *Derivations

GetDerivations returns the Derivations value for PropDerivations, or nil if absent.

func (PropertyMap) GetOrdering

func (m PropertyMap) GetOrdering() Ordering

GetOrdering returns the Ordering value for PropOrdering, or zero Ordering.

type ProvidedOrderingPart

type ProvidedOrderingPart struct {
	Value     values.Value
	SortOrder ProvidedSortOrder
}

ProvidedOrderingPart is a (Value, ProvidedSortOrder) pair for a provided ordering element.

func (ProvidedOrderingPart) NaturalComparisonKeyValue

func (p ProvidedOrderingPart) NaturalComparisonKeyValue(reverse bool) (values.Value, bool)

NaturalComparisonKeyValue returns the executable comparison-key Value for this ordering part when the set operation can compare the raw Value in its chosen direction.

Java's ProvidedOrderingPart.comparisonKeyValue(reverse) returns the raw Value only when the resulting tuple direction is ASC_NULLS_FIRST:

  • a forward merge over ASC_NULLS_FIRST parts; or
  • a reverse merge over DESC_NULLS_LAST parts.

Every other combination needs ToOrderedBytesValue to encode direction and/or counterflow NULL placement into the physical key. Go carries that Value shape, but its evaluator is not implemented yet. Returning ok=false keeps set-operation construction fail-closed instead of comparing nil or raw values with the wrong physical ordering.

type ProvidedSortOrder

type ProvidedSortOrder int

ProvidedSortOrder represents the sort direction (and NULL placement) of a provided ordering part. Mirrors Java's OrderingPart.ProvidedSortOrder — exactly 6 constants, each directional value mapping to a TupleOrdering.Direction:

Ascending            -> ASC_NULLS_FIRST  (counterflow=false)
Descending           -> DESC_NULLS_LAST  (counterflow=false)
AscendingNullsLast   -> ASC_NULLS_LAST   (counterflow=true)
DescendingNullsFirst -> DESC_NULLS_FIRST (counterflow=true)
Fixed / Choose       -> non-directional
const (
	ProvidedSortOrderAscending ProvidedSortOrder = iota
	ProvidedSortOrderDescending
	ProvidedSortOrderAscendingNullsLast
	ProvidedSortOrderDescendingNullsFirst
	ProvidedSortOrderFixed
	ProvidedSortOrderChoose
)

func SortOrderOf

func SortOrderOf(bindings []OrderingBinding) ProvidedSortOrder

SortOrder returns the aggregate sort order of a value's bindings. If all bindings are sorted with the same direction, returns that direction. If mixed or all fixed, returns ProvidedSortOrderFixed.

func (ProvidedSortOrder) IsAnyAscending

func (s ProvidedSortOrder) IsAnyAscending() bool

IsAnyAscending reports whether this is an ascending direction (nulls first or last). Mirrors Java SortOrder.isAnyAscending().

func (ProvidedSortOrder) IsAnyDescending

func (s ProvidedSortOrder) IsAnyDescending() bool

func (ProvidedSortOrder) IsCompatibleWithRequestedSortOrder

func (p ProvidedSortOrder) IsCompatibleWithRequestedSortOrder(req RequestedSortOrder) bool

IsCompatibleWithRequestedSortOrder checks if a provided sort order is compatible with a requested sort order. Ports Java OrderingPart.ProvidedSortOrder.isCompatibleWithRequestedSortOrder (OrderingPart.java:322-332) exactly:

  • ANY request, or a CHOOSE/FIXED provided order, is always compatible (and these early-returns must come BEFORE the counterflow check, since IsCounterflowNulls is only meaningful for directional values);
  • otherwise the NULL placement must agree (counterflow-nulls equality);
  • and the direction (ascending-ness) must agree, compared via IsAnyAscending on BOTH sides (so e.g. ASC_NULLS_LAST provided is compatible with an ASC_NULLS_LAST request, not just plain ASCENDING).

This is the gate that keeps a forward (ASC_NULLS_FIRST) scan from satisfying an `ORDER BY x ASC NULLS LAST` (ASC_NULLS_LAST) request, so the sort is not wrongly elided.

func (ProvidedSortOrder) IsCounterflowNulls

func (s ProvidedSortOrder) IsCounterflowNulls() bool

IsCounterflowNulls reports whether the NULL placement runs against the natural tuple order for this direction. Mirrors Java TupleOrdering.Direction.isCounterflowNulls(). Only valid for directional values.

func (ProvidedSortOrder) IsDirectional

func (s ProvidedSortOrder) IsDirectional() bool

func (ProvidedSortOrder) ToRequestedSortOrder

func (s ProvidedSortOrder) ToRequestedSortOrder() RequestedSortOrder

ToRequestedSortOrder maps a provided sort order to the requested sort order with the same TupleOrdering.Direction (preserving NULL placement), mirroring Java ProvidedSortOrder.toRequestedSortOrder() (a by-Direction map, not a collapse to the natural order).

type RecordTypeProvider

type RecordTypeProvider interface {
	GetRecordTypes() []string
}

RecordTypeProvider is the interface expressions implement to expose the record types they produce. Matches Java's RecordQueryScanPlan.getRecordTypes(), FullUnorderedScanExpression. getRecordTypes(), etc.

type ReferencesAndDependencies

type ReferencesAndDependencies struct {
	// Refs is the set of all References found in the subtree.
	Refs map[*expressions.Reference]struct{}
	// Dependencies maps a Reference to the set of References it
	// depends on (i.e. ref -> {child refs it quantifies over}).
	Dependencies map[*expressions.Reference]map[*expressions.Reference]struct{}
}

ReferencesAndDependencies captures the set of References in an expression subtree and the dependency edges between them. Matches Java's ReferencesAndDependenciesProperty result type (PartiallyOrderedSet<Reference>) in a simplified form.

func EvaluateReferencesAndDependencies

func EvaluateReferencesAndDependencies(expr expressions.RelationalExpression) ReferencesAndDependencies

EvaluateReferencesAndDependencies walks the expression tree and collects all References and their dependency relationships. Matches Java's ReferencesAndDependenciesProperty.evaluate.

At each Reference node, the visitor adds the Reference itself to the set and records dependency edges from that Reference to each child Reference it quantifies over.

func EvaluateReferencesAndDependenciesForRef

func EvaluateReferencesAndDependenciesForRef(ref *expressions.Reference) ReferencesAndDependencies

EvaluateReferencesAndDependenciesForRef evaluates starting from a Reference. Matches Java's evaluate(Reference) overload.

type RequestedOrdering

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

RequestedOrdering captures a desired sort order for a query's output. Used to communicate ordering requirements upward (toward sources) during planning. Mirrors Java's RequestedOrdering.

func CombineRequestedOrderings

func CombineRequestedOrderings(current, added []*RequestedOrdering) ([]*RequestedOrdering, bool)

CombineRequestedOrderings unions `new` into `current` with subsumption, porting Java RequestedOrderingConstraint.combine exactly (RequestedOrderingConstraint.java:38-76): a new ordering already subsumed by a current one (same distinctness; an exhaustive current subsumes any new whose parts extend its prefix; otherwise exact part equality) is dropped. ok=false means nothing new remained — the existing constraint already covers the push (Java's Optional.empty, where the caller leaves the map untouched).

func NewRequestedOrdering

func NewRequestedOrdering(parts []RequestedOrderingPart, d Distinctness, exhaustive bool) *RequestedOrdering

NewRequestedOrdering creates a new RequestedOrdering.

func PreserveOrdering

func PreserveOrdering() *RequestedOrdering

Preserve returns a RequestedOrdering that preserves the incoming order.

func (*RequestedOrdering) Exhaustive

func (o *RequestedOrdering) Exhaustive() *RequestedOrdering

Exhaustive returns a copy of this ordering with exhaustive=true. If already exhaustive, returns the receiver unchanged. Ports Java's RequestedOrdering.exhaustive().

func (*RequestedOrdering) GetDistinctness

func (o *RequestedOrdering) GetDistinctness() Distinctness

GetDistinctness returns the distinctness constraint.

func (*RequestedOrdering) GetParts

func (o *RequestedOrdering) GetParts() []RequestedOrderingPart

GetParts returns the ordering parts.

func (*RequestedOrdering) GetValueRequestedSortOrderMap

func (o *RequestedOrdering) GetValueRequestedSortOrderMap() map[values.Value]RequestedSortOrder

GetValueRequestedSortOrderMap returns a map from Value to its requested sort order.

func (*RequestedOrdering) IsDistinct

func (o *RequestedOrdering) IsDistinct() bool

IsDistinct returns true if the ordering requires distinct records.

func (*RequestedOrdering) IsExhaustive

func (o *RequestedOrdering) IsExhaustive() bool

IsExhaustive returns true if we want all satisfying plans, not just the best.

func (*RequestedOrdering) IsPreserve

func (o *RequestedOrdering) IsPreserve() bool

IsPreserve returns true if this ordering requests preservation of the existing order (empty parts).

func (*RequestedOrdering) PushDownThroughValue

func (o *RequestedOrdering) PushDownThroughValue(resultValue values.Value, upperAlias values.CorrelationIdentifier) *RequestedOrdering

PushDownThroughValue translates this requested ordering's keys through a result value, expressing them in terms of the result value's inputs. Each ordering part's value is pushed down; sort orders are preserved. Parts that cannot be pushed down are dropped. If all parts are dropped, returns a preserve ordering.

Ports Java's RequestedOrdering.pushDown(Value, CorrelationIdentifier, EvaluationContext, AliasMap, Set<CorrelationIdentifier>).

func (*RequestedOrdering) Size

func (o *RequestedOrdering) Size() int

Size returns the number of ordering parts.

type RequestedOrderingPart

type RequestedOrderingPart struct {
	Value     values.Value
	SortOrder RequestedSortOrder
}

RequestedOrderingPart is a (Value, RequestedSortOrder) pair specifying one element of a requested ordering. Mirrors Java's OrderingPart.RequestedOrderingPart.

type RequestedSortOrder

type RequestedSortOrder int

RequestedSortOrder specifies the desired sort direction (and NULL placement) for one ordering part. Mirrors Java's OrderingPart.RequestedSortOrder, where each directional value maps to a TupleOrdering.Direction:

Ascending            -> ASC_NULLS_FIRST  (ascending,  nulls first; counterflow=false)
Descending           -> DESC_NULLS_LAST  (descending, nulls last;  counterflow=false)
AscendingNullsLast   -> ASC_NULLS_LAST   (ascending,  nulls last;  counterflow=true)
DescendingNullsFirst -> DESC_NULLS_FIRST (descending, nulls first; counterflow=true)

"Counterflow nulls" means the requested NULL placement runs against the natural FDB tuple order for that direction (ASC defaults nulls-first, DESC defaults nulls-last). This is the property Java's ProvidedSortOrder.isCompatibleWithRequestedSortOrder gates on.

const (
	RequestedSortOrderAny RequestedSortOrder = iota
	RequestedSortOrderAscending
	RequestedSortOrderDescending
	RequestedSortOrderAscendingNullsLast
	RequestedSortOrderDescendingNullsFirst
)

func (RequestedSortOrder) IsAnyAscending

func (s RequestedSortOrder) IsAnyAscending() bool

IsAnyAscending reports whether this is an ascending direction (nulls first or last). Mirrors Java SortOrder.isAnyAscending().

func (RequestedSortOrder) IsAnyDescending

func (s RequestedSortOrder) IsAnyDescending() bool

IsAnyDescending reports whether this is a descending direction (nulls first or last). Mirrors Java SortOrder.isAnyDescending().

func (RequestedSortOrder) IsCounterflowNulls

func (s RequestedSortOrder) IsCounterflowNulls() bool

IsCounterflowNulls reports whether the requested NULL placement runs against the natural tuple order for this direction. Mirrors Java TupleOrdering.Direction.isCounterflowNulls(). Only valid for directional values (Java verifies isDirectional first).

func (RequestedSortOrder) IsDirectional

func (s RequestedSortOrder) IsDirectional() bool

type RichOrdering

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

RichOrdering captures a provided ordering as a binding map (Value → []OrderingBinding) plus a partially ordered set of value keys and a distinctness flag.

The ordering set is a PartiallyOrderedSet[string] keyed by ExplainValue(v). A reverse map resolves keys back to values.

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

func ConcatOrderings

func ConcatOrderings(outer, inner *RichOrdering) *RichOrdering

ConcatOrderings concatenates two orderings using proper partial-order semantics. The maximum elements of the left ordering become dependencies of the minimum elements of the right ordering (for non-fixed keys).

func CreateUnionOrdering

func CreateUnionOrdering(o *RichOrdering) *RichOrdering

CreateUnionOrdering creates a RichOrdering from a single provided ordering, treating all sorted bindings as union-compatible. Used as the starting point for union-merge in ImplementDistinctUnionRule.

func EmptyOrdering

func EmptyOrdering() *RichOrdering

EmptyOrdering returns an ordering with no keys.

func MergeOrderings

func MergeOrderings(a, b *RichOrdering) *RichOrdering

MergeOrderings merges two orderings (union semantics, backward-compat alias).

func MergeOrderingsForIntersection

func MergeOrderingsForIntersection(a, b *RichOrdering) *RichOrdering

MergeOrderingsForIntersection merges two orderings with intersection semantics.

func MergeOrderingsForUnion

func MergeOrderingsForUnion(a, b *RichOrdering) *RichOrdering

MergeOrderingsForUnion merges two orderings with union semantics. Uses the full EligibleSet-based merge algorithm from Java.

func NewRichOrdering

func NewRichOrdering(
	bindingMap map[values.Value][]OrderingBinding,
	keys []values.Value,
	distinct CoordinateBoundClaim,
) *RichOrdering

NewRichOrdering creates a new ordering from bindings, key sequence, and distinctness. The ordering set is a total order matching the key sequence.

func NewRichOrderingWithDeps

func NewRichOrderingWithDeps(
	bindingMap map[values.Value][]OrderingBinding,
	keys []values.Value,
	deps combinatorics.SetMultimap[string],
	distinct CoordinateBoundClaim,
) *RichOrdering

NewRichOrderingWithDeps creates an ordering with explicit dependency edges in the ordering set.

func (*RichOrdering) DirectionalOrderingParts

func (o *RichOrdering) DirectionalOrderingParts(
	keyValues []values.Value,
	requested *RequestedOrdering,
	fixedOrder ProvidedSortOrder,
) []ProvidedOrderingPart

DirectionalOrderingParts creates ProvidedOrderingParts from a key sequence with sort directions from the binding map. Fixed keys get the provided fixedOrder direction.

func (*RichOrdering) DistinctnessClaim

func (o *RichOrdering) DistinctnessClaim() CoordinateBoundClaim

DistinctnessClaim returns this ordering's distinctness claim still bound to THIS ordering's coordinates, for a consumer that is constructing a derived ordering. Because the coordinates travel with it, a derived ordering that dropped any of them reports IsDistinct false without the deriving site having to notice.

func (*RichOrdering) EnumerateCompatibleRequestedOrderings

func (o *RichOrdering) EnumerateCompatibleRequestedOrderings(
	requested *RequestedOrdering,
) [][]RequestedOrderingPart

EnumerateCompatibleRequestedOrderings enumerates all valid orderings of the full ordering set that are compatible with the requested ordering prefix. Each result is a full-length sequence of RequestedOrderingParts (one per key in the ordering set).

func (*RichOrdering) EnumerateSatisfyingComparisonKeyValues

func (o *RichOrdering) EnumerateSatisfyingComparisonKeyValues(
	requested *RequestedOrdering,
) [][]values.Value

EnumerateSatisfyingComparisonKeyValues enumerates the comparison key sequences from this ordering that satisfy the requested ordering. Uses TopologicalSort.satisfyingPermutations on the partial order to find all valid orderings.

func (*RichOrdering) EnumerateSatisfyingIntersectionComparisonKeyValues

func (o *RichOrdering) EnumerateSatisfyingIntersectionComparisonKeyValues(
	requested *RequestedOrdering,
) [][]values.Value

EnumerateSatisfyingIntersectionComparisonKeyValues enumerates comparison keys with Java's intersection semantics. Equality-bound values remain in the merged ordering's binding metadata, but they do not participate in the physical merge key: every surviving row has one constant value there.

This intentionally differs from EnumerateSatisfyingComparisonKeyValues, whose older Go callers treat o.keys as the physical key wholesale. Java's SetOperationsOrdering filters to singular non-fixed values, removes fixed values from the requested prefix, and then enumerates full topological permutations of that reduced set.

func (*RichOrdering) GetBindingMap

func (o *RichOrdering) GetBindingMap() map[values.Value][]OrderingBinding

GetBindingMap returns the binding map.

func (*RichOrdering) GetEqualityBoundValues

func (o *RichOrdering) GetEqualityBoundValues() map[values.Value]struct{}

GetEqualityBoundValues returns the set of values that have at least one fixed (equality-bound) binding. Matches Java's Ordering.getEqualityBoundValues() which uses Multimaps.filterValues(Binding::isFixed).keySet().

func (*RichOrdering) GetKeys

func (o *RichOrdering) GetKeys() []values.Value

GetKeys returns the ordering keys in sequence.

func (*RichOrdering) GetOrderingKeys

func (o *RichOrdering) GetOrderingKeys() []values.Value

GetOrderingKeys returns the non-equality-bound ordering keys (those that contribute to the sort order). Mirrors Java's Ordering.getOrderingSet().getSet() filtered to the keys list.

func (*RichOrdering) IsDistinct

func (o *RichOrdering) IsDistinct() bool

IsDistinct returns whether the ordering guarantees distinct output — that is, whether its carried claim still holds over the coordinates it actually has. A claim proved over a longer key set does not answer for a reduced one.

func (*RichOrdering) IsSingularNonFixedValue

func (o *RichOrdering) IsSingularNonFixedValue(v values.Value) bool

IsSingularNonFixedValue returns true if the value has exactly one binding and that binding is not fixed (sorted or choose).

func (*RichOrdering) OrderingSet

OrderingSet returns the partially ordered set of value keys.

func (*RichOrdering) PullUp

func (o *RichOrdering) PullUp(mapping map[string]values.Value) *RichOrdering

PullUp translates this ordering through a string-keyed value mapping. For each ordering key, if the mapping contains a replacement (keyed by ExplainValue string), the key is renamed. Bindings are preserved. Keys not in the mapping are dropped.

This is the simpler of two pull-up paths:

  • PullUp(mapping): string-keyed FieldValue-to-FieldValue renaming. Used by projection rules and callers that build an explicit rename map.
  • PullUpThroughValue(resultValue, alias): full Value-tree-aware pull-up via values.PullUpValue. Handles RecordConstructorValue decomposition and passthrough (QOV/ObjectValue) semantics. Used by callers that have a result-value structure rather than a flat rename map.

Both paths produce correct results for their use cases. Callers should prefer PullUpThroughValue when the result value is available, as it handles nested value structures (e.g. record constructors) that a flat string map cannot represent.

func (*RichOrdering) PullUpThroughValue

func (o *RichOrdering) PullUpThroughValue(resultValue values.Value, alias values.CorrelationIdentifier) (*RichOrdering, error)

PullUpThroughValue translates this ordering through a result value. For each ordering key, uses Value.PullUpValue to compute the pulled-up key. Value-backed fixed comparison operands are translated through the same result value; literal, parameter, and opaque fixed bindings are unchanged. A dynamic fixed binding that cannot be translated is dropped, and a key whose last binding is dropped cannot contribute an ordering above the projection.

Ports Java's Ordering.pullUp(Value, EvaluationContext, AliasMap, Set<CorrelationIdentifier>) using the direct algorithmic pullUp from values.PullUpValue.

func (*RichOrdering) PushDown

func (o *RichOrdering) PushDown(mapping map[string]values.Value) *RichOrdering

PushDown is the inverse of PullUp — translates ordering keys back through a reverse mapping.

func (*RichOrdering) PushDownThroughValue

func (o *RichOrdering) PushDownThroughValue(resultValue values.Value, upperAlias values.CorrelationIdentifier) *RichOrdering

PushDownThroughValue translates this ordering's keys from output space back to input space through a result value. For each ordering key, uses Value.PushDownValue to compute the pushed-down key. Value-backed fixed comparison operands are pushed down through the same result value; literal, parameter, and opaque fixed bindings are unchanged. Keys that cannot be pushed down, or whose last dynamic fixed binding cannot be pushed down, are dropped.

Ports Java's Ordering.pushDown(Value, EvaluationContext, AliasMap, Set<CorrelationIdentifier>).

func (*RichOrdering) Satisfies

func (o *RichOrdering) Satisfies(requested *RequestedOrdering) bool

Satisfies checks whether this provided ordering satisfies the given requested ordering. Uses the partial order to enumerate valid permutations that match the request prefix.

func (*RichOrdering) SatisfiesGroupingValues

func (o *RichOrdering) SatisfiesGroupingValues(groupingValues map[string]struct{}) bool

SatisfiesGroupingValues checks whether the given set of values can form a valid prefix of some topological ordering of this ordering set.

func (*RichOrdering) StorageKeyIsComplete

func (o *RichOrdering) StorageKeyIsComplete() bool

StorageKeyIsComplete reports whether this ordering's coordinates are the whole storage key, answered against the coordinates it actually has. A truncated, prefixed, or reshaped ordering answers false even when the producer's original claim was true.

func (*RichOrdering) ValueForKey

func (o *RichOrdering) ValueForKey(key string) values.Value

ValueForKey resolves a string key back to its Value.

func (*RichOrdering) WithStorageKeyComplete

func (o *RichOrdering) WithStorageKeyComplete(complete bool) *RichOrdering

WithStorageKeyComplete stamps (or withholds) the completeness claim. It is the PRODUCER's call and no one else's: the site that assembled the coordinate list is the site that knows whether it truncated one, and every consumer that re-derived the answer from key component types was asking a second property to agree with this one by hand.

type RichOrderingHinter

type RichOrderingHinter interface {
	HintRichOrdering() *RichOrdering
}

RichOrderingHinter is the optional interface an expression implements to provide its full ordering WITH bindings — the equality-bound (fixed) vs. merely sorted distinction that plain Ordering cannot express. Callers that find it absent synthesize a binding map from HintOrdering instead.

It lives here, beside OrderingHinter, so that both the physical wrappers in cascades and the plans (which cannot import cascades) can implement it.

type ScanComparisonProvider

type ScanComparisonProvider interface {
	GetScanComparisons() []*predicates.ComparisonRange
}

ScanComparisonProvider is the interface plan nodes implement to expose their per-column scan comparison ranges — the Go analogue of Java's RecordQueryPlanWithComparisons, off which ComparisonsProperty reads a node's own comparisons. An index scan and a primary scan both provide them.

type StatisticsProvider

type StatisticsProvider interface {
	// RecordTypeCardinality returns the estimated row count for
	// `recordTypeName`. Returns LeafScanCardinality if the record
	// type is unknown — implementations should NOT return zero or
	// negative values.
	RecordTypeCardinality(recordTypeName string) float64
}

StatisticsProvider is the planner's hook for table-level cardinality statistics. Returns the estimated row count for a given record type name. Implementations:

  • DefaultStatistics — every record type returns LeafScanCardinality. Used when no real stats are available.
  • Catalog-backed (future) — reads `COUNT` index counters from the RecordMetaData for the queried record store.
  • Test stubs — return programmable per-name values to drive calibration tests.

Implementations must be safe for concurrent calls (the planner may query the same provider from multiple goroutines).

Jump to

Keyboard shortcuts

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