executor

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

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

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

Documentation

Overview

Package executor bridges RecordQueryPlan trees (Cascades planner output) and the FDBRecordStore scanning API to produce RecordCursor[QueryResult] streams. Mirrors Java's RecordQueryPlan.executePlan dispatching to FDBRecordStoreBase.scanRecords.

The executor is a standalone visitor (not a method on RecordQueryPlan) to avoid circular dependencies between the plans package and the recordlayer package.

Index

Constants

View Source
const DefaultMaxSortBufferRows = 5_000_000

DefaultMaxSortBufferRows is the maximum number of rows the in-memory sort cursor will materialize before returning an error. Prevents OOM on queries that sort unbounded result sets without LIMIT. Override per cursor via the maxBuf field.

Variables

This section is empty.

Functions

func AssertLegColumnProvenanceCensus

func AssertLegColumnProvenanceCensus(w io.Writer, floors *LegColumnProvenanceFloors) bool

AssertLegColumnProvenanceCensus checks the census's partition, its one zero and its population floors, and reports whether it failed.

The partition is the point: every share this census prints is a share of Calls, and a share only means something if the shares add up. The zero is DottedHitIdentityDiverged — a leg whose text and whose stated identity name different things, resolved by the text. That is not a residue to shrink, it is a contradiction: two keys for one leg, disagreeing, with only the weaker one consulted.

floors is nil when the run is NARROWED, exactly as its siblings do it: the floors describe a whole-suite population, and a -test.run selecting tests that never adapt a leg row reaches this reader zero times. The partition and the zero still run — they hold over any population, which is precisely why they are not a proof on their own.

func EvaluateScalarSubquery

func EvaluateScalarSubquery(
	ctx context.Context,
	plan plans.RecordQueryPlan,
	store *recordlayer.FDBRecordStore,
	evalCtx *EvaluationContext,
	props recordlayer.ExecuteProperties,
) (any, error)

EvaluateScalarSubquery executes a scalar subquery plan and returns its single scalar result. SQL standard semantics:

  • Exactly one column (else error)
  • At most one row (else 21000 cardinality violation)
  • Zero rows → nil (SQL NULL)

Used by the Cascades executor to pre-evaluate uncorrelated scalar subqueries before running the outer plan.

func ExecutePlan

ExecutePlan executes a RecordQueryPlan tree against a store, returning a cursor over the results. Recursive — child plans are executed first, then the parent operator is applied.

func FormatLegColumnProvenanceCensus

func FormatLegColumnProvenanceCensus() string

FormatLegColumnProvenanceCensus renders the census for a harness to log.

func FormatMergedLegBindingCensus

func FormatMergedLegBindingCensus() string

FormatMergedLegBindingCensus renders the census for a harness to log.

The headline is the RATIO: binds are what the path costs, reads are what it buys. Reporting only the bind count would read as evidence the binder is carrying the corpus, which is the misreading this census was built to correct.

func LegColumnProvenanceCensus

func LegColumnProvenanceCensus() (legColumnProvenanceCounters, []string)

LegColumnProvenanceCensus reports the counters and the retained witnesses.

func LegColumnProvenanceDottedNames

func LegColumnProvenanceDottedNames() []string

LegColumnProvenanceDottedNames returns the distinct COLUMN NAMES the dotted arm ANSWERED on — the qualified labels, not the witness prose.

It exists so a cross-population claim can be checked rather than eyeballed. The retirement condition booked for this reader is "the dotted-hit count goes to 0", booked against converting a mint in the TRANSLATOR; whether that is reachable depends on whether the names that mint produces are these names. Comparing the two sets needs both as data, and this census's witnesses are sentences.

func MergedLegBindingCensus

func MergedLegBindingCensus() (map[MergedLegBinding]int, map[string]int)

MergedLegBindingCensus returns copies of the bind and read tallies.

func MergedRowLegReads

func MergedRowLegReads() map[MergedRowRead]int

MergedRowLegReads returns a copy of the MULTI-LEG subset of the read tally — the reads that resolved to a window on a merged row carrying sibling legs, keyed by (alias, merged-row shape).

func PositionalTypeForDescriptor

func PositionalTypeForDescriptor(desc protoreflect.MessageDescriptor) *values.RecordType

PositionalTypeForDescriptor returns the LOGICAL RecordType for a message descriptor — one field per descriptor field in declaration order (the field's ordinal), UPPER-cased name, declared column type. THE single authority for a stored record's logical row shape: every physical access path that serves a stored record's columns (base scan rows via protoToPositional, covering-index rows via coveringIndexCursor) MUST shape its rows by this type, so a plan-time LOGICAL ordinal reads the same slot on every path (mirrors Java's IndexKeyValueToPartialRecord, which reconstructs a descriptor-shaped partial record for exactly this reason). Cached per descriptor (see positionalTypeCache).

func PositionalTypeForRecordLayout

func PositionalTypeForRecordLayout(desc protoreflect.MessageDescriptor, storeRowVersions bool) *values.RecordType

PositionalTypeForRecordLayout is the metadata-aware row-shape authority: the descriptor's positional type, extended with the __ROW_VERSION pseudo-slot when the store's metadata stores row versions and the descriptor does not declare a REAL field of that name (real-column-wins, Java Type.Record.addPseudoFields, Type.java:2358-2368). Every planner- or runtime-facing derivation of a stored record's row layout must go through this so plan-time ordinals and runtime slots agree on version-storing stores.

func RedundantMergedLegReaders

func RedundantMergedLegReaders() map[MergedRowRead]string

RedundantMergedLegReaders returns a copy of the proven-redundant registry, each shape rendered as its proofs' names in sorted order (the suite is parallel, so registration order is not a stable thing to report).

func RegisterRedundantMergedLegReader

func RegisterRedundantMergedLegReader(read MergedRowRead, why string)

RegisterRedundantMergedLegReader records that reads of ONE (alias, merged-row shape) were proven not load-bearing in this run — the same rows under a perturbation of the binder's window — by the named proof. Call it ONLY from the passing path of that proof.

The perturbation is the proof's own choice and the two live ones differ: declining the window entirely so the alias resolves to nothing (TestFDB_MergedLegBinding_ReaderShapeIsRedundant), and aiming every window at a sibling leg's span (TestFDB_MergedLegBinding_WrongWindowsAreUnobservable). Both establish the same thing about the same reads — the value behind the binding does not reach an answer — so both excuse them, and both are named.

It takes the read's full identity, not its alias, so the excusal covers exactly the shape the proof ran. A proof of `ST` out of an `ST[0,3)|OT[3,5)` merged row says nothing about `ST` out of some other merged row, and a registry keyed on the name would have said it anyway.

why is the test's own identity, carried so the activation gate can name what is excusing a read rather than asserting it on the census's authority.

func ResetMergedLegBindingCensus

func ResetMergedLegBindingCensus()

ResetMergedLegBindingCensus clears all tallies.

func RowValue

func RowValue(qr QueryResult) any

RowValue returns a QueryResult's row in name-keyed form: a bare scalar for a 1-slot `_0` row (a non-record UNNEST element), else a name->value map (duplicate output names collapse last-wins). EXPORTED for external test/differential consumers only; production code reads the PositionalRow by ordinal. Nil-safe.

func UnshadowedMergedRowLegReads

func UnshadowedMergedRowLegReads() map[MergedRowRead]int

UnshadowedMergedRowLegReads returns a copy of the UNSHADOWED subset — multi-leg reads of a window that displaced nothing, so the binder was the alias's only binding.

This is a DIAGNOSTIC, not the activation gate's input: over the sqldriver corpus every multi-leg read is unshadowed and none is load-bearing, so the structural property does not separate the alarm case from the quiet one. See mergedLegUnshadowedMergedRowReads for the measurement that settled it. The accessor exists so FormatMergedLegBindingCensus can report the number that makes the point, and so a future change of that number is visible rather than invisible.

Types

type AggregateTypeMismatchError

type AggregateTypeMismatchError struct {
	Message string
}

AggregateTypeMismatchError is returned when MIN or MAX is applied to a non-numeric column. Java's fdb-relational rejects this with "VerifyException: unable to encapsulate aggregate operation due to type mismatch(es)" — the function registry only installs numeric MIN/MAX overloads.

func (*AggregateTypeMismatchError) Error

type ColumnDef

type ColumnDef struct {
	Name     string // output column name (positional slot / by-name lookup key)
	Label    string // display name (alias); empty means use Name
	TypeName string // JDBC type name: BIGINT, STRING, DOUBLE, etc.
	Nullable int    // api.ColumnNoNulls / ColumnNullable / ColumnNullableUnknown
}

ColumnDef describes one column in the result set.

type EvaluationContext

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

EvaluationContext holds runtime bindings for plan execution: parameter values, correlation bindings (for correlated subqueries), scalar subquery results, and any mutable state that plan nodes share. Mirrors Java's EvaluationContext.

func EmptyEvaluationContext

func EmptyEvaluationContext() *EvaluationContext

EmptyEvaluationContext returns a context with no bindings.

func (*EvaluationContext) BindParameter

func (ec *EvaluationContext) BindParameter(ordinal int, name string) (any, bool)

BindParameter implements values.ParameterBinder. Ordinal is 1-based; named parameters are not yet supported.

func (*EvaluationContext) GetBinding

func (ec *EvaluationContext) GetBinding(id values.CorrelationIdentifier) (any, bool)

GetBinding retrieves a correlation binding.

func (*EvaluationContext) GetCorrelationBinding

func (ec *EvaluationContext) GetCorrelationBinding(id values.CorrelationIdentifier) (any, bool)

GetCorrelationBinding implements values.CorrelationBinder so that QuantifiedObjectValue can resolve correlated rows during scan comparison evaluation in the FlatMap execution path.

func (*EvaluationContext) GetOrCreateTempTable

GetOrCreateTempTable returns the TempTable at the given alias, creating one if it doesn't exist. Mutates ec.bindings directly (intentional — temp tables are shared mutable state across the execution, not copy-on-write like WithBinding). Callers must ensure this is called on the root context, not on a WithBinding copy.

st is the statement's ExecuteState (RFC-130) charged when a temp table is freshly created here; an already-bound temp table keeps its original state (it was minted with the same statement's state). Callers pass props.State.

func (*EvaluationContext) ReleaseAllTempTableCharges

func (ec *EvaluationContext) ReleaseAllTempTableCharges()

ReleaseAllTempTableCharges releases every temp table bound in THIS context's map back to the statement budget — the teardown half of the recursion's live-bytes accounting (the DFS accumulator is minted into the shared bindings map by GetOrCreateTempTable, so the recursion cannot name it directly). Idempotent per table (the tally zeroes). A nested recursion sharing the map would release an outer recursion's still-live tables a step early — an accepted under-account bounded by the outer working set; the outer teardown's release is then a harmless no-op.

func (*EvaluationContext) RowContext

func (ec *EvaluationContext) RowContext() *values.RowEvalContext

RowContext returns a binding-only RowEvalContext — this context's parameter bindings, correlation bindings, and scalar subquery results, with NO frontier row. Used when evaluating expressions that reference only params / correlations / scalar subqueries; a row-bearing context flows through RowContextPositional.

func (*EvaluationContext) RowContextPositional

func (ec *EvaluationContext) RowContextPositional(pos values.OrdinalRow) *values.RowEvalContext

RowContextPositional returns a RowEvalContext whose authoritative row is the ordinal-model positional row (resolved by ordinal, no name-map fallback), combined with this context's parameter bindings, correlation bindings, and scalar subquery results. Use it on the non-join frontier when a param / scalar subquery / outer correlation is in play; when none is, flow the bare OrdinalRow directly. An outer correlation resolves via Correlations first; only the (unbound) frontier quantifier reference falls to the positional row.

func (*EvaluationContext) Scratch

func (ec *EvaluationContext) Scratch() *ExecutionScratch

Scratch returns the statement's execution scratch, or nil when the execution carries none.

func (*EvaluationContext) StatementNow

func (ec *EvaluationContext) StatementNow() time.Time

StatementNow implements values.StatementClock: the one instant every CURRENT_TIMESTAMP-family reference in this statement observes. An unstamped context degrades to the wall clock — that is the per-row drift the stamp exists to prevent, so statement entry points must stamp via WithStatementTime.

func (*EvaluationContext) WithBinding

WithBinding returns a shallow copy with an additional binding.

func (*EvaluationContext) WithExecutionScratch

func (ec *EvaluationContext) WithExecutionScratch(s *ExecutionScratch) *EvaluationContext

WithExecutionScratch returns a copy carrying the statement's scratch. The statement's paging loop stamps this once and reuses it for every page, which is what lets an operator hand its live resume state to the next page instead of serializing it. Like every other With* copy, it rides all derived contexts.

func (*EvaluationContext) WithMergedLegReadBypass

func (ec *EvaluationContext) WithMergedLegReadBypass(aliases ...string) *EvaluationContext

WithMergedLegReadBypass returns a copy in which lookups of the named aliases DECLINE any window bindMergedOuterLegs produced, falling back to whatever that window displaced (nothing, for a window that displaced nothing).

It exists for one caller: the redundancy pin, which has to run the SAME query down BOTH resolution routes and compare. A build-tagged neuter cannot do that job — it removes the binder from the whole binary, so the two routes never coexist in one run, and their agreement is the entire content of the pin.

Honoured only while the leg-identity census gate is on (the read site's gate), so production never consults it.

func (*EvaluationContext) WithMergedLegReadSink

func (ec *EvaluationContext) WithMergedLegReadSink(sink *MergedLegReadSink) *EvaluationContext

WithMergedLegReadSink returns a copy whose binder-window reads and declined lookups are ALSO recorded into sink, scoped to this execution.

Honoured only while the leg-identity census gate is on, like the rest of this instrumentation.

func (*EvaluationContext) WithMergedLegWrongWindows

func (ec *EvaluationContext) WithMergedLegWrongWindows() *EvaluationContext

WithMergedLegWrongWindows returns a copy in which bindMergedOuterLegs aims each leg window of a merged row at its SIBLING's span rather than its own, so every binding a reader can resolve through is DELIBERATELY WRONG.

It exists so the mutation that licenses "the merged-leg bindings are not load-bearing" can STAND rather than be re-run by hand. That claim rested on someone editing the binder to misaim it and watching the suite stay green; nothing performed it in CI, so the day a read starts depending on which slots its window covers, no test went red and a green census read as "the bindings are correct" when it only ever meant "nobody looked".

It rides EvaluationContext for the same two reasons WithMergedLegReadBypass does: the suite is parallel and several tests share these table names, so a process-wide switch would misaim a concurrently running test's execution; and an edited-out or build-tagged misaim cannot run the correct and the wrong window in ONE process, which is the entire comparison.

The perturbation is a ROTATION onto the sibling's span, not a constant offset, because a constant is not reliably wrong: the first leg of a merged row already starts at 0, so "point everything at slot 0" leaves it aimed correctly. Rotation moves every window of a multi-leg row whose legs are not all identically shaped, and the windows it did move are the ones counted — an instrument that cannot state it perturbed anything proves nothing.

Honoured only while the leg-identity census gate is on, like the rest of this instrumentation, so production never misaims.

func (*EvaluationContext) WithParams

func (ec *EvaluationContext) WithParams(params []any) *EvaluationContext

WithParams returns a copy with prepared-statement parameter bindings. Params is 0-indexed; ParameterValue ordinals are 1-based. The copy CARRIES scalarSubqueries like every other With* copy — dropping them would make binding ORDER load-bearing (WithScalarSubqueries().WithParams() would silently unbind the subqueries, and unbound is a loud *values.UnboundScalarSubqueryError at row time).

func (*EvaluationContext) WithScalarSubqueries

func (ec *EvaluationContext) WithScalarSubqueries(results map[values.CorrelationIdentifier]any) *EvaluationContext

WithScalarSubqueries returns a copy with pre-evaluated scalar subquery results bound by correlation alias.

func (*EvaluationContext) WithStatementTime

func (ec *EvaluationContext) WithStatementTime(t time.Time) *EvaluationContext

WithStatementTime returns a copy stamped with the statement-stable CURRENT_TIMESTAMP-family instant. Like every other With* copy, the stamp rides all derived contexts (WithParams, WithBinding, …).

type ExecutionScratch

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

ExecutionScratch is the statement-scoped home for operator resume state that is too large to serialize into every page's continuation.

WHY IT EXISTS. A continuation is normally self-contained: the operator packs whatever it needs to resume into bytes, and the bytes alone rebuild it. That is right when the state is O(1) — the streaming distinct's single previous key, a scan's position. It is quadratic when the state is O(rows already emitted), which is exactly the unordered hash distinct: its seen-set is the operator's whole memory, so page P's continuation would carry every key emitted through page P and a P-page drain would serialize and re-parse O(P^2) keys in total. Measured on a 401-page drain of 400 distinct values, the continuation grew 9 -> 1749 bytes and the drain re-parsed ~350 KB of keys to emit 400 rows.

The state cannot be shrunk: exact dedup over UNORDERED input fundamentally needs the set (an ordered input is the case that needs only the last key, and the planner already routes it to the streaming executor). Java declines to pay either cost — RecordQueryUnorderedPrimaryKeyDistinctPlan.java:100-104 mints `new HashSet<>()` per execution and passes the inner's continuation through untouched, so a duplicate spanning a resume is silently re-admitted. Go does not accept wrong rows, so the set has to survive the page boundary; the only remaining choice is whether it survives THROUGH bytes or BESIDE them. Beside them is the one that is not quadratic.

WHY IT IS SOUND. The bytes are round-tripped by exactly one production caller, the SQL statement's paging loop (cascades_generator.go), which re-executes the same plan against a fresh transaction to respect FDB's 5s bound. Its continuations never escape the statement: the driver rejects statement continuations outright (Go SQL tokens are engine-private and no resume entry point exists). So a statement-scoped side channel has exactly the lifetime the bytes do, and a resume that finds no scratch entry is an impossible state rather than a supported one — it is reported as an error, never deduped against an empty set.

THE INVARIANT THAT MAKES IT SAFE: **executing a page must be idempotent with respect to the scratch.** Lifetime is not the whole story — the same continuation bytes are not merely HELD for the statement, they are EXECUTED more than once. paginatingRows.fetchPage runs its whole body inside the FDB retry loop (runInCapturedTx -> DB.Run -> TransactCtx, fdb/database.go:323), which re-invokes the closure from the UNCHANGED r.continuation on any retryable error; the closure resets r.buf, so the failed attempt's rows are discarded. A scratch that let the failed attempt mutate shared state would therefore lose rows silently — the retry would treat the discarded attempt's values as already emitted. (Reproduced: a mid-drain abort on a 12-value fixture emitted 10, no error. The by-value encoding was immune by construction, because bytes are immutable.) Two rules restore it:

  1. A published set is IMMUTABLE. A resumed cursor never touches the set it adopted; it accumulates its own page's new keys in a private delta and publishes base+delta as a NEW entry. A dying attempt then mutates nothing its retry can see. The base is never COPIED per page — that would restore the quadratic — it is extended in place exactly once, when rule 2 proves no one can reach its earlier form again.

  2. Eviction keys on ADOPT, never on PARK. Reaching token T proves the page that minted T committed and was consumed, so T's predecessor is dead. MINTING T proves nothing: the attempt that minted it can still fail, and its retry resumes from the predecessor it was minted from. (Reproduced: park-time eviction turned a routine retryable error after a completed page into a hard "seen-set not held" failure.) Evicting on adopt keeps the bound at a couple of live entries per operator all the same.

RETIREMENT FOLLOWS NAMEABILITY. An entry must live exactly as long as some continuation the statement still holds can name its token — no longer, and not one moment less. Three earlier designs keyed retirement on a CURSOR LIFECYCLE EVENT instead, and each was wrong for its own reason. They are recorded because the wrong answer is the intuitive one:

  • on PARK: a retry resumes the predecessor the failed attempt was minted from, so minting cannot retire it.
  • on a page-local MARK of the surviving continuation: enclosing continuation objects cache their own bytes, so the final ToBytes need never call down and the mark is missed. It silently dropped rows.
  • on EXHAUSTION: an enclosing operator can retain a child continuation OBJECT past the child's end and serialize it later — aggregateCursor.emitFinal emits its final row carrying a retained earlier inner position (streaming_cursors.go:393-401). A token minted before the inner ended is still named after it.

Object-graph reachability from the surviving continuation is what this WANTED to be, and the code forbids it: sixteen sites wrap a child by serializing it eagerly into a plain BytesContinuation (limitEnvelopeCursor, executor.go:2009, is the one that matters here), so the child object is dropped and no walk — however careful about byte caching — can see through it. Bytes are the only medium that survives, so ADOPTION is the only ground truth available: a page adopts exactly the entries its own continuation named. SweepAfterPage is built on that, at the cost of one page of lag, plus the exact case that needs no lag (an exhausted page names nothing at all).

"A page adopts exactly what its continuation names" has ONE hole, and it is plugged explicitly rather than argued away: a page can FORWARD a name instead of resolving it, handing bytes it received straight back out without ever building the cursor that would adopt them. holdForwardedName is how the forwarding site says so, and the sweep then judges nothing unreachable that page. A forwarded name is a name.

Residency is then bounded by that sweep plus two structural rules:

  1. An entry belongs to a CURSOR, not to a continuation (the prefix length rides the continuation instead), so an operator that serializes a child continuation on EVERY emitted row adds one entry, not one per row.
  2. Adoption evicts the predecessor and any sibling a failed attempt published from it, so a paging chain keeps two.

MEMORY. The statement is charged for exactly what the scratch holds: the COMMITTED BASE plus the LIVE DELTAS. A parked entry holds its delta's bytes for as long as it lives — the producing cursor hands its page charge over at park time instead of releasing it at close — and retirement gives them back. Folding charges the committed layer permanently, because that layer really is held for the statement.

Both halves are load-bearing and they cancel if only one is present, which is how they were both wrong at once while every test passed. Without the fold charge the committed set weighs nothing, so a high-cardinality DISTINCT can never trip the budget and the loud-failure promise is empty. Without every removal returning its bytes — and there are three doors out of s.distinct, so they all go through retire — the charge tracks how many PAGES a statement took rather than how much data it holds, and a retry-heavy or finely-paged statement fails for memory it does not have. Measured on 40 distinct values before the fix: 77 bytes at 1 row/page, 71 at 3, 59 at 10, and 0 in a single page.

Concurrency: the executor is single-threaded per statement (zero goroutine launches in this package, pinned by package_invariant_test.go), so the maps need no lock, exactly as ExecuteState's counters do not.

func NewExecutionScratch

func NewExecutionScratch() *ExecutionScratch

NewExecutionScratch mints a scratch for one statement. A nil *ExecutionScratch is valid and means "no scratch": operators then fall back to self-contained continuations, which is correct and merely quadratic.

func (*ExecutionScratch) BeginPage

func (s *ExecutionScratch) BeginPage()

BeginPage tells the scratch a page's execution is starting: it DISCARDS what a previous attempt of this same page parked, then snapshots the token high-water mark and clears the page-scoped records. Safe on a nil scratch.

THE DISCARD IS THE OTHER HALF OF RETRY IDEMPOTENCE. This runs inside the FDB retry loop's body (cascades_generator.go:2002), so a second call without an intervening SweepAfterPage means the previous attempt's transaction DIED: its rows were dropped and its continuation bytes with them. Everything it parked is therefore named by nothing — and it is CHARGED, because a cursor hands its delta charge to its entry at park time and the close hook deliberately does not release it. Leaving it behind made a conflict storm accumulate one attempt-sized delta per attempt, with nothing reclaiming any of it until the page finally committed, so a statement could fail its memory budget (or the process) before any attempt got through. Measured over five attempts of one page: 16, 22, 28, 34, 40 bytes.

Dropping it is safe for exactly the reason the published set is immutable: an attempt only ever EXTENDS a committed layer it adopted read-only, and the retry re-adopts that same layer from the same bytes. It cannot reach the dead attempt's delta, because nothing it holds names it.

This is not the eviction rule 2 forbids. Rule 2 is about the entry a retry RESUMES FROM — its predecessor, parked by a committed page and named by the unchanged continuation bytes the retry re-executes; that entry is at or below settled and is never touched here.

func (*ExecutionScratch) LiveDistinctSets

func (s *ExecutionScratch) LiveDistinctSets() int

LiveDistinctSets returns how many parked states the scratch still holds. Test/diagnostic accessor; production code never reads it. It is what pins the eviction bound — the scratch must not accumulate one entry per page.

func (*ExecutionScratch) MintedDistinctSets

func (s *ExecutionScratch) MintedDistinctSets() int64

MintedDistinctSets returns how many seen-sets have been parked in this scratch. Test/diagnostic accessor (like ExecuteState.MemUsed); production code never reads it. It exists because the statement layer's wiring of the scratch is otherwise INVISIBLE: dropping WithExecutionScratch would leave every row-level assertion green while silently restoring the quadratic continuation, so the wiring needs an observable of its own.

func (*ExecutionScratch) PeakDistinctSets

func (s *ExecutionScratch) PeakDistinctSets() int

PeakDistinctSets returns the high-water mark of simultaneously live parked states. Test/diagnostic accessor (like MintedDistinctSets); production code never reads it.

It exists because the damage a failed attempt does is INVISIBLE from outside a page: the attempt's leftovers are collected by the page's own sweep once it finally commits, so every between-pages sample — the only kind a statement's caller can take — reads the same as an unfaulted run. The high-water mark is what a retried page actually costs while it is being retried, which is the figure a conflict storm multiplies.

func (*ExecutionScratch) SweepAfterPage

func (s *ExecutionScratch) SweepAfterPage(exhausted bool)

SweepAfterPage retires the entries the completed page has made unreachable.

RETIREMENT FOLLOWS NAMEABILITY, and the only thing that can name a token is a byte string the statement still holds. The statement holds exactly one — the page's surviving continuation — so:

  • exhausted: nothing is resumable at all, so NOTHING is nameable. Every entry retires. This is what bounds a single-page statement, including one whose correlated inner ran thousands of times.
  • otherwise: an entry parked BEFORE this page began and not adopted DURING it is unreachable, because a page adopts exactly the entries its own continuation named. Entries parked by this page are kept: which of them the surviving continuation names is not knowable yet, and the next page's adoptions are what settle it.

Deliberately NOT object-graph reachability from the surviving continuation, which is the shape this wanted to be: enclosing operators serialize their child eagerly and hand back a plain BytesContinuation (limitEnvelopeCursor, executor.go:2009, plus fifteen other sites), so the child object is dropped and no walk can see through it. Adoption is the only ground truth that survives that, at the cost of one page of lag.

Retiring on a CURSOR lifecycle event instead — park, close, exhaustion — is the mistake this replaced three times over. Exhaustion in particular does not imply un-nameability: aggregateCursor.emitFinal emits its final row carrying a RETAINED earlier inner continuation (streaming_cursors.go:393-401), so a token minted before the inner exhausted is still named after it.

Safe on a nil scratch.

type FilteredIndexPlanError

type FilteredIndexPlanError struct {
	IndexName string
}

FilteredIndexPlanError reports a physical query plan that attempts to read a sparse/filtered index without carrying a predicate-implication proof. The planner currently excludes every filtered index from query candidates, so a plan that reaches this guard is hand-built or stale. Executing it would read only the predicate-selected subset and could silently omit records.

Low-level record-store APIs deliberately remain able to scan filtered indexes for maintenance and diagnostics. This is a query-plan invariant.

func (*FilteredIndexPlanError) Error

func (e *FilteredIndexPlanError) Error() string

type IncompatiblePhysicalComparandError

type IncompatiblePhysicalComparandError struct {
	Component     int
	Comparison    predicates.ComparisonType
	PhysicalType  values.Type
	ProjectedType string
}

IncompatiblePhysicalComparandError reports an evaluated scan comparand that cannot inhabit the authoritative physical key component's tuple carrier. UNKNOWN parameters deliberately pass planner type gates, so this check must happen after valid packing-boundary coercions (numeric width and UUID) but before a mismatched tuple type code can turn SQL UNKNOWN into arbitrary rows.

func (*IncompatiblePhysicalComparandError) Error

type InvalidScanComparisonShapeError

type InvalidScanComparisonShapeError struct {
	Component int
	Detail    string
}

InvalidScanComparisonShapeError reports a comparison slice that cannot be represented by one contiguous tuple-key prefix: zero or more equalities, optionally one inequality component, and then only nil/Empty components. Silently stopping at the first gap or tail would discard a later predicate while presenting the scan as fully SARGed.

func (*InvalidScanComparisonShapeError) Error

type InvalidScanRangeExecutionIdentityTypeError

type InvalidScanRangeExecutionIdentityTypeError struct {
	Kind           ScanIdentityTypeErrorKind
	Implementation string
	Path           string
	ActivePath     string
}

InvalidScanRangeExecutionIdentityTypeError reports an invalid values.Type graph while a continuation fingerprint is being built. It deliberately records only concrete implementation names and structural paths: formatting the Type itself could invoke String on the same malformed graph and recurse.

func (*InvalidScanRangeExecutionIdentityTypeError) Error

type LegColumnProvenanceFloors

type LegColumnProvenanceFloors struct {
	// Calls floors the denominator every share is taken against.
	Calls int
	// DottedHitIdentityAvailable floors the population the retirement decision
	// is ABOUT. Calls alone does not cover it: the flat-hit arm carries 40 of
	// the 52 calls, so Calls can stay healthy while the dotted arm — the only
	// arm this census exists for — goes to zero.
	DottedHitIdentityAvailable int
}

LegColumnProvenanceFloors is the minimum population this census must report over a whole suite run.

It exists for the reason its two siblings' floors exist, and the reason is sharper here than for either of them. This census's entire finding is a pair of small numbers — the dotted arm answers FOUR times in the whole corpus, and all four have an identity available — and the retirement decision rests on BOTH halves: on the four being all there is, and on all four being identity-available. A zero population satisfies the second half vacuously. It also satisfies the DottedHitIdentityDiverged zero vacuously, and the partition as 0 == 0.

So a census that stopped being driven at all reports exactly the shape of a census reporting good news, and the numbers are small enough that "4" and "0" do not look different at a glance. The floors are what make them different.

Set at 1 rather than an order of magnitude below the measurement, because there is no order of magnitude here: the measured DottedHitIdentityAvailable is 4. What a floor detects at this scale is DISAPPEARANCE, which is the whole failure mode — the shapes that drive the dotted arm ceasing to be planned, or the reader ceasing to be reached.

type MaterializationLimitExceededError

type MaterializationLimitExceededError struct {
	Limit   int
	Context string
}

MaterializationLimitExceededError is returned when an operator tries to buffer more rows in memory than the configured materialization limit.

func (*MaterializationLimitExceededError) Error

type MergedLegBinding

type MergedLegBinding struct {
	// OuterAlias is the FlatMap's own alias — the join whose merged row is being
	// decomposed. It is recorded because the join's-own-alias skip is decided
	// against it, and on the live shapes it never engages (the box's alias is not
	// any leg's alias), which is exactly the case the unit fixtures did not cover.
	OuterAlias string
	LegAlias   string
	Offset     int
	Width      int
}

The MERGED-LEG BINDING census: what bindMergedOuterLegs PRODUCED, and whether anything READ it.

It exists because those two are different questions and the answer differs. The binder was documented as having "no non-test consumer", which read as dormant. It is not dormant — it executes on the real-FDB corpus on every clustered-box gather, over ten thousand times in one sqldriver run. It also has a READER, on a multi-leg merged row: over that same run its bindings are looked up twelve times, every one of them on a merged row carrying SIBLING legs. What those reads are not is LOAD-BEARING — neither removing the bindings entirely nor aiming every window at a sibling leg's slots changes a single assertion, and both of those perturbations are performed by a standing test on every run rather than by hand.

A claim like that decays the moment someone adds a load-bearing consumer, and it decays SILENTLY — the binder keeps working, so nothing goes red, and the next person reads the stale claim and reasons from it. So the claim is measured here rather than asserted in a comment: the reads are counted, attributed to the merged row they came out of, and excused one shape at a time by a proof that re-runs. A read this census cannot match to such a proof turns the gate red and says what gets re-armed.

It is GATED on the same flag as the leg-identity census (values.LegIdentityCensusEnabled): the read side sits in EvaluationContext.GetCorrelationBinding, which is per-reference-per-row, and production must not pay a type assertion there.

type MergedLegReadSink

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

MergedLegReadSink collects ONE execution's binder-window reads and declined lookups, in parallel with the process-global census.

It exists because the global census cannot answer "what did THIS execution do?". Its tallies are process-wide and the suite is parallel, so a before/after delta around one execution also picks up whatever a concurrently running test read in the same window — and the redundancy pin's whole method is to compare two executions of one query by what each of them read. A delta that a sibling test can perturb makes that comparison intermittently wrong in the direction of a spurious red.

The zero value is not usable; a nil *MergedLegReadSink is, and records nothing, so the read path can call through it unconditionally.

func NewMergedLegReadSink

func NewMergedLegReadSink() *MergedLegReadSink

NewMergedLegReadSink returns an empty sink.

func (*MergedLegReadSink) BypassOutcomes

func (s *MergedLegReadSink) BypassOutcomes() (misses, handoffs map[MergedRowRead]int)

BypassOutcomes returns copies of this execution's declined lookups: those that resolved to NOTHING, and those handed a binding the window had displaced.

func (*MergedLegReadSink) MisaimedReads

func (s *MergedLegReadSink) MisaimedReads() map[MergedRowRead]int

MisaimedReads returns a copy of the subset of this execution's reads that resolved to a window misaimMergedLegWindows had moved off its own leg.

func (*MergedLegReadSink) Reads

func (s *MergedLegReadSink) Reads() map[MergedRowRead]int

Reads returns a copy of this execution's multi-leg binder-window reads.

type MergedRowRead

type MergedRowRead struct {
	Alias string
	// Shape is mergedRowShape of the merged row the read's window looked into.
	Shape string
}

MergedRowRead identifies a read that resolved to a binder-produced window on a MULTI-LEG merged row: the alias that was read, AND the LAYOUT of the merged row it was read out of.

The layout is carried because the alias alone is not an identity. Alias names COLLIDE across queries — the corpus binds twenty unrelated legs under the outer name `X` — which is why this census refuses to classify reads by alias. The same argument applies with full force to EXCUSING them: a proven-redundant entry keyed on the bare name `ST` excuses every future multi-leg read of anything called `ST`, including a load-bearing one in a query nobody has written yet. Keyed on (alias, shape) it excuses only what the proof actually covered.

type NumericRangeOverflowError

type NumericRangeOverflowError struct {
	Value    any
	Column   string
	TypeName string
}

func (*NumericRangeOverflowError) Error

func (e *NumericRangeOverflowError) Error() string

type PositionalRow

type PositionalRow struct {
	// Type gives each slot its name and type; Slots[i] is the value of the field
	// at ordinal i. len(Slots) == len(Type.Fields) for a well-formed row.
	Type  *values.RecordType
	Slots []any
}

PositionalRow is the typed positional runtime row — the SOLE runtime row: field values indexed by ORDINAL, paired with the RecordType that names and types each slot. Positional access (Slots[ordinal]) mirrors Java's MessageHelpers.getFieldValueForFieldOrdinals; every column reference reads its plan-time-baked ordinal (there is no runtime name resolution — the Type's names serve plan-time binding and diagnostics).

func NewPositionalRow

func NewPositionalRow(typ *values.RecordType) *PositionalRow

NewPositionalRow builds a row for typ with every slot nil (SQL NULL). Slots is sized to the field count so Get/Set are position-safe. A nil typ yields an empty row (zero slots).

func (*PositionalRow) Get

func (r *PositionalRow) Get(ordinal int) (any, bool)

Get returns the value at the given ordinal plus an in-range flag. Nil-safe.

func (*PositionalRow) MultiLeg

func (r *PositionalRow) MultiLeg() bool

MultiLeg reports whether this row is a MULTI-LEG composed row (a merged concat / clustered box row whose Type carries leg boundaries beyond a single whole-row window). Consulted by values.FieldValue's correlated fall-through arms: a source-relative baked ordinal cannot be served by a multi-leg row without a leg binding — such a read must fail loud rather than silently address the wrong leg's slot.

func (*PositionalRow) Set

func (r *PositionalRow) Set(ordinal int, v any) bool

Set writes v at the given ordinal, returning false (no-op) if out of range.

func (*PositionalRow) TypeNames

func (r *PositionalRow) TypeNames() []string

TypeNames returns the row type's column names in ordinal order — diagnostics for values.OrdinalResolutionError (via an optional-interface assertion), so a loud resolution miss reports what the row actually carried.

type QueryResult

type QueryResult struct {
	// Positional is the ordinal-model row (a typed PositionalRow, field
	// values indexed by ordinal) — the SOLE runtime row. Every producer emits it
	// (scans/covering scans, projection/map, aggregate output, and join merges
	// via concatLegPositionals), and FieldValue resolution reads it by ordinal,
	// loud on a miss (OrdinalResolutionError). There is no name-keyed row model:
	// runtime name resolution would be first-match and silently wrong on
	// duplicate column names, where the plan-time ordinal is exact.
	Positional *PositionalRow
	Record     *recordlayer.FDBStoredRecord[proto.Message]
	PrimaryKey tuple.Tuple
}

QueryResult is the row type flowing through plan execution cursors. Wraps the ordinal-model row (Positional), an optional stored record (when the row originated from a scan), and an optional primary key. Mirrors Java's QueryResult.

func CollectAll

func CollectAll(ctx context.Context, cursor recordlayer.RecordCursor[QueryResult]) ([]QueryResult, error)

CollectAll drains a cursor into a slice.

func CollectAllBounded

func CollectAllBounded(ctx context.Context, cursor recordlayer.RecordCursor[QueryResult], st *recordlayer.ExecuteState, limit int, opName string) ([]QueryResult, int64, error)

CollectAllBounded drains a cursor into a slice through an accounted boundedBuffer (RFC-130): every row is charged against the statement-wide memory byte budget (st) AND counted against the row-count materialization limit, so a missed accumulation site is impossible — the buffer cannot exist without the accountant. st is the always-present statement ExecuteState (props.State); a nil/zero-limit st makes the byte charge a no-op while the row-count cap still applies. Returns MaterializationLimitExceededError on the row cap and MemoryLimitExceededError (→ 54F01) on the byte budget. The second return is the total bytes charged against st — the owner of the returned rows releases exactly that at teardown (live-bytes model: the buffer is rebuilt per page against the statement-wide state, so an unreleased charge re-accumulates once per page).

func FromStoredRecord

func FromStoredRecord(rec *recordlayer.FDBStoredRecord[proto.Message]) QueryResult

FromStoredRecord builds a QueryResult from a stored record. The row is the ordinal PositionalRow built from the proto message (protoToPositional — one slot per descriptor field in declaration order; FieldValue reads it by ordinal).

When the record's store stores row versions, the row is extended with the trailing __ROW_VERSION pseudo-slot carrying the record version's 12 bytes (nil when the record has no version) — the runtime half of Java's PseudoField.fillInIfApplicable (PseudoField.java:72-100), keyed on the same two gates: the metadata stores versions, and the descriptor does not define a REAL field of that name (real-column-wins).

type RecordLayerResultSet

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

RecordLayerResultSet wraps a RecordCursor[QueryResult] and implements api.ResultSet. Mirrors Java's RecordLayerResultSet: Next() advances the cursor, typed accessors read from the current row's positional slots.

Column metadata is provided at construction time (derived from the plan's result type or the schema catalog). Column accessors are 1-indexed per JDBC convention.

func NewRecordLayerResultSet

func NewRecordLayerResultSet(
	ctx context.Context,
	cursor recordlayer.RecordCursor[QueryResult],
	columns []ColumnDef,
) *RecordLayerResultSet

NewRecordLayerResultSet constructs a ResultSet from an executor cursor and column definitions.

func (*RecordLayerResultSet) Boolean

func (rs *RecordLayerResultSet) Boolean(columnIndex int) (bool, error)

func (*RecordLayerResultSet) BooleanByName

func (rs *RecordLayerResultSet) BooleanByName(name string) (bool, error)

func (*RecordLayerResultSet) Bytes

func (rs *RecordLayerResultSet) Bytes(columnIndex int) ([]byte, error)

func (*RecordLayerResultSet) BytesByName

func (rs *RecordLayerResultSet) BytesByName(name string) ([]byte, error)

func (*RecordLayerResultSet) Close

func (rs *RecordLayerResultSet) Close() error

func (*RecordLayerResultSet) Continuation

func (rs *RecordLayerResultSet) Continuation() (api.Continuation, error)

func (*RecordLayerResultSet) Double

func (rs *RecordLayerResultSet) Double(columnIndex int) (float64, error)

func (*RecordLayerResultSet) Err

func (rs *RecordLayerResultSet) Err() error

func (*RecordLayerResultSet) Float

func (rs *RecordLayerResultSet) Float(columnIndex int) (float32, error)

func (*RecordLayerResultSet) GetContinuation

GetContinuation returns the raw cursor continuation from the last Next() call. Used by the paginating execution loop to resume across FDB transactions.

func (*RecordLayerResultSet) GetNoNextReason

func (rs *RecordLayerResultSet) GetNoNextReason() recordlayer.NoNextReason

GetNoNextReason returns the NoNextReason from the last Next() call. This is the AUTHORITATIVE exhaustion signal (SourceExhausted ⇔ end-of-results), and distinguishes a non-terminal out-of-band stop (scan/time/byte limit) from a clean ReturnLimitReached/exhaustion when the continuation has nil bytes — see RFC-127 (Java carries noNextReason as a first-class field for exactly this; its nil-byte START continuation is otherwise ambiguous with end).

func (*RecordLayerResultSet) Long

func (rs *RecordLayerResultSet) Long(columnIndex int) (int64, error)

func (*RecordLayerResultSet) LongByName

func (rs *RecordLayerResultSet) LongByName(name string) (int64, error)

func (*RecordLayerResultSet) MetaData

func (*RecordLayerResultSet) Next

func (rs *RecordLayerResultSet) Next() bool

func (*RecordLayerResultSet) Object

func (rs *RecordLayerResultSet) Object(columnIndex int) (any, error)

func (*RecordLayerResultSet) ObjectByName

func (rs *RecordLayerResultSet) ObjectByName(name string) (any, error)

func (*RecordLayerResultSet) String

func (rs *RecordLayerResultSet) String(columnIndex int) (string, error)

func (*RecordLayerResultSet) StringByName

func (rs *RecordLayerResultSet) StringByName(name string) (string, error)

func (*RecordLayerResultSet) WasNull

func (rs *RecordLayerResultSet) WasNull() bool

type RecursiveCTEDepthExceededError

type RecursiveCTEDepthExceededError struct {
	MaxDepth int
}

func (*RecursiveCTEDepthExceededError) Error

type ScanIdentityTypeErrorKind

type ScanIdentityTypeErrorKind uint8

ScanIdentityTypeErrorKind identifies why a values.Type cannot safely participate in a scan continuation identity. The encoder accepts arbitrary implementations of values.Type, including hand-built plans, so it must reject interface values that would otherwise panic or recurse without end.

const (
	ScanIdentityTypeErrorTypedNil ScanIdentityTypeErrorKind = iota + 1
	ScanIdentityTypeErrorCycle
)

type SortBufferExceededError

type SortBufferExceededError struct {
	Rows  int
	Limit int
}

SortBufferExceededError is returned when an in-memory sort materializes more rows than the configured limit. Prevents OOM on unbounded ORDER BY without LIMIT.

func (*SortBufferExceededError) Error

func (e *SortBufferExceededError) Error() string

type SumOverflowError

type SumOverflowError struct{ Int32 bool }

SumOverflowError reports a SUM/AVG accumulation leaving its operator's integer domain. Int32 selects the SUM_I/AVG_I lane's message: Java's Math.addExact(int, int) throws ArithmeticException("integer overflow") where the long overload throws "long overflow", and the messages surface verbatim through the driver (both under SQLSTATE 22003).

func (*SumOverflowError) Error

func (e *SumOverflowError) Error() string

type TempTable

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

TempTable is an in-memory list of QueryResult used by TempTableInsertPlan and TempTableScanPlan. Mirrors Java's com.apple.foundationdb.record.TempTable.

RFC-130: a TempTable is a cardinality-growing buffer — the recursive-CTE per-level working set (ping-ponged scan/insert tables) and the TempTableInsertPlan target both accumulate into it, separate from the CollectAllBounded per-level materialization. It carries the statement's always-present *ExecuteState (st) and charges each appended row's byte estimate in Add. The pre-existing sync.Mutex is defensive (the zero- goroutine executor invariant makes it currently moot); charging under the lock is correct regardless — if the executor ever goes concurrent the pinned package_invariant_test fires and ChargeMemory moves to atomic.

func NewTempTable

func NewTempTable() *TempTable

NewTempTable creates an empty temp table with no memory budget. Used by internal call sites that have no statement ExecuteState in scope (and by tests); production statement paths use NewTempTableWithState so the statement-wide memory budget covers the temp-table working set.

func NewTempTableWithState

func NewTempTableWithState(st *recordlayer.ExecuteState) *TempTable

NewTempTableWithState creates an empty temp table that charges its rows against the supplied statement ExecuteState (RFC-130). st is the always- present statement state; a nil/zero-limit st makes the charge a no-op.

func (*TempTable) Add

func (tt *TempTable) Add(qr QueryResult) error

Add appends a QueryResult to the temp table, charging its byte estimate against the statement memory budget first (RFC-130). On a budget breach the row is NOT appended and the *MemoryLimitExceededError is returned.

func (*TempTable) Clear

func (tt *TempTable) Clear()

Clear removes all entries from the temp table. Allocates a fresh backing array (tt.list = nil, not tt.list[:0]) rather than truncating in place — see Snapshot's doc comment for why: reusing the old array would let a still-unread Snapshot silently see whatever level's rows get Add-ed next.

func (*TempTable) GetList

func (tt *TempTable) GetList() []QueryResult

GetList returns a COPY of the temp table contents (safe to hold across any later mutation, at the cost of an O(len) copy on every call). Prefer Snapshot for a read that will be held across an unbounded number of future Add calls without needing that copy.

func (*TempTable) ReleaseCharges

func (tt *TempTable) ReleaseCharges()

ReleaseCharges returns every byte this table has charged to the statement budget and zeroes the tally. Called exactly once by the table's owner at teardown; idempotent because the tally is zeroed.

func (*TempTable) ReplaceList

func (tt *TempTable) ReplaceList(rows []QueryResult)

ReplaceList replaces the temp-table contents with rows that have ALREADY been charged against the statement memory budget — it does NOT re-charge. Used by the recursive-CTE DISTINCT path, which filters the rows the recursive plan already inserted (and charged via Add) down to the non-duplicate subset; re-charging them through Add would double-count the same resident rows. memUsed is monotonic, so the rows dropped by the filter stay charged (a conservative ceiling) — that is intentional and correct. Allocates a fresh backing array for the same reason as Clear (see Snapshot): appending rows[:0] would let a still-unread Snapshot alias this replacement's writes.

func (*TempTable) Snapshot

func (tt *TempTable) Snapshot() []QueryResult

Snapshot returns the table's current row slice with NO copy — O(1), just the three-word slice header. This is safe to hold indefinitely (across any number of subsequent Add calls, in particular across a lazily-serialized continuation that may never be read, or read long after further rows were added) because of an invariant the rest of this type maintains: Add only ever appends past the length a Snapshot already captured (so old data a Snapshot exposes is never overwritten by later growth), and Clear/ ReplaceList always hand tt.list a FRESH backing array rather than truncating the existing one in place — so nothing this table does after a Snapshot was taken can ever become visible through it. Same principle as mergeSortCursor's per-child continuation snapshot (executor_new_plans.go): a held reference is safe because the type never mutates what it already exposed, only replaces it wholesale.

That safety is one-directional: it covers what THIS TYPE does to the backing array, not what a careless caller could do to it. The returned slice aliases tt's own memory — never write through it (no element assignment, no append() within its existing capacity: both land exactly where Add's own next append would write, corrupting rows Add hasn't produced yet). Treat the result as read-only. Call GetList instead if you need a slice you're allowed to mutate.

type UnknownPhysicalKeyTypeError

type UnknownPhysicalKeyTypeError struct {
	Component  int
	Comparison predicates.ComparisonType
}

UnknownPhysicalKeyTypeError reports a non-null scan comparand for which the chosen physical plan did not carry an authoritative key-component type. Tuple encoding is selected by the Go runtime carrier, so guessing from an operand declaration or evaluated parameter can probe a different tuple type code than the stored key (most notably FLOAT versus DOUBLE). Type-independent NULL cases are handled before this error is considered.

func (*UnknownPhysicalKeyTypeError) Error

type UnsupportedContinuationError

type UnsupportedContinuationError struct {
	Shape string
}

UnsupportedContinuationError reports a resume attempt on a cursor shape that has no continuation support yet (RFC-180 WS-A follow-ups). The driver maps it to SQLSTATE 0A000 — a typed decline, never a silent wrong start (before RFC-180 the buffered union fed the PARENT's continuation to every child; a raw-key scan child consumed it as a scan position).

func (*UnsupportedContinuationError) Error

type UnsupportedPhysicalFloatEquivalenceError

type UnsupportedPhysicalFloatEquivalenceError struct {
	Component    int
	Comparison   predicates.ComparisonType
	PhysicalType values.Type
}

UnsupportedPhysicalFloatEquivalenceError reports a floating-point scan comparand whose logical equality class cannot yet be represented exactly by this physical range binder. FDB tuple keys preserve every NaN sign/payload, while the query value comparator treats NaNs as one logical value. Probing the evaluated payload would silently miss equal keys, so an already-chosen dynamic index access fails before opening storage.

func (*UnsupportedPhysicalFloatEquivalenceError) Error

type UnsupportedPhysicalKeyTypeError

type UnsupportedPhysicalKeyTypeError struct {
	Component    int
	Comparison   predicates.ComparisonType
	PhysicalType values.Type
}

UnsupportedPhysicalKeyTypeError reports physical metadata that names a placeholder or structured logical type rather than one concrete scalar FDB tuple carrier. Presence is not authority: ANY, NULL, NONE, RECORD, ARRAY, RELATION, and ENUM do not specify how a query comparand was encoded in the key. Production metadata maps enum fields to their LONG wire carrier; a hand-built/stale plan that carries ENUM itself must therefore fail closed.

func (*UnsupportedPhysicalKeyTypeError) Error

type UnsupportedPhysicalNumericProjectionError

type UnsupportedPhysicalNumericProjectionError struct {
	Component            int
	Comparison           predicates.ComparisonType
	PhysicalType         values.Type
	EquivalenceClassLow  int64
	EquivalenceClassHigh int64
}

UnsupportedPhysicalNumericProjectionError reports a floating-point comparand whose logical equality class contains more than one value in an authoritative physical integer key domain. The scan plan's fixed-key and one-row proofs are valid only when every successful equality binding selects at most one physical integer key. Returning this error before storage is therefore preferable to silently probing one representative of a larger cmpAny equivalence class.

The class can contain multiple integers because cmpAny compares a mixed integer/float pair after converting the integer to float64. For example, both 2^53 and 2^53+1 compare equal to float64(2^53). Ordered comparisons do not use this error: their monotone inverse is projected to an exact inclusive integer interval by projectFloatComparisonToIntegerDomain.

func (*UnsupportedPhysicalNumericProjectionError) Error

type UnsupportedPhysicalStartsWithError

type UnsupportedPhysicalStartsWithError struct {
	Component    int
	PhysicalType values.Type
}

UnsupportedPhysicalStartsWithError reports a STARTS_WITH comparison whose authoritative physical key component is not STRING. PREFIX_STRING tuple endpoints describe a byte prefix of a packed string element; applying them to another carrier (including BYTES, or the string-backed DATE/TIMESTAMP logical types) does not implement Comparison.Eval's string/string STARTS_WITH truth table. Such a plan must fail before opening storage rather than silently treating an operator/type mismatch as a compensated range.

func (*UnsupportedPhysicalStartsWithError) Error

Jump to

Keyboard shortcuts

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