Documentation
¶
Overview ¶
Package batch provides the core columnar data structures for the execution engine.
Index ¶
- Constants
- func DecodeContainerColumn(payload []byte, v *Vector, n int) error
- func EncodeContainerColumn(dst []byte, v *Vector, n int) ([]byte, error)
- func FormatDate(days int32) string
- func FormatTimestamp(ms int64) string
- func FormatValue(v any) string
- func IsContainerType(t TypeID) bool
- func NewMintOwner() uint64
- func PoisonOnRelease() bool
- func PoisonedBatches() uint64
- func SetPoisonOnRelease(on bool) bool
- func SyncContainerSchema(b *RecordBatch)
- type BatchPool
- type Bitmap
- func (b *Bitmap) CopyFrom(src *Bitmap, n int)
- func (b *Bitmap) EnsureLen(n int)
- func (b Bitmap) Grow(newLen int) Bitmap
- func (b *Bitmap) HasNulls() bool
- func (b *Bitmap) InvalidateCache()
- func (b *Bitmap) IsNull(i int) bool
- func (b *Bitmap) IsNullFast(i int) bool
- func (b *Bitmap) Len() int
- func (b *Bitmap) NullCount() int
- func (b *Bitmap) ResetNonNull(n int)
- func (b *Bitmap) SetNull(i int)
- func (b *Bitmap) SetNullRange(start, count int)
- func (b *Bitmap) SetValid(i int)
- func (b *Bitmap) Words() []uint64
- type BytesColumn
- func (dst *BytesColumn) BulkCopy(dstOff int, src *BytesColumn, srcOff, count int)
- func (bc *BytesColumn) BulkSet(dstOffset int, srcData []byte, srcOffsets []uint32, n int)
- func (bc *BytesColumn) Len() int
- func (bc *BytesColumn) LengthAt(i int) int
- func (bc *BytesColumn) MemBytes() int64
- func (bc *BytesColumn) PreAllocBytes(n int)
- func (bc *BytesColumn) Reset()
- func (bc *BytesColumn) ResetForWrite(n int)
- func (bc *BytesColumn) Set(i int, val []byte)
- func (dst *BytesColumn) SetFrom(di int, src *BytesColumn, si int)
- func (bc *BytesColumn) SetString(i int, val string)
- func (bc *BytesColumn) StringValue(i int) string
- func (bc *BytesColumn) UnsafeStringValue(i int) string
- func (bc *BytesColumn) Value(i int) []byte
- type DecimalColumn
- type GlobalPool
- type Int128
- func (d Int128) Add(other Int128) Int128
- func (d Int128) BigInt() *big.Int
- func (d Int128) Equal(other Int128) bool
- func (d Int128) FormatDecimal(scale int) string
- func (d Int128) IsNegative() bool
- func (d Int128) IsZero() bool
- func (d Int128) Less(other Int128) bool
- func (d Int128) MulPow10(n int) (Int128, bool)
- func (d Int128) Neg() Int128
- func (d Int128) Sub(other Int128) Int128
- func (d Int128) ToFloat64(scale int) float64
- func (d Int128) ToInt64() int64
- type MintStamp
- type RecordBatch
- func (b *RecordBatch) ActiveLen() int
- func (b *RecordBatch) ColumnByName(name string) *Vector
- func (b *RecordBatch) ColumnIndex(name string) int
- func (b *RecordBatch) Compact() *RecordBatch
- func (b *RecordBatch) Detach()
- func (b *RecordBatch) DetachPool()
- func (b *RecordBatch) EnsureCapacity(n int)
- func (b *RecordBatch) FlattenColumn(i int)
- func (b *RecordBatch) FlattenViews()
- func (b *RecordBatch) HasViews() bool
- func (b *RecordBatch) MemBytes() int64
- func (b *RecordBatch) Mint() MintStamp
- func (b *RecordBatch) Release()
- func (b *RecordBatch) Reset(numRows int)
- func (b *RecordBatch) Retained() bool
- func (b *RecordBatch) RowAt(i int) map[string]any
- func (b *RecordBatch) SetMint(m MintStamp)
- func (b *RecordBatch) ToRows() []map[string]any
- type TypeID
- type TypeMismatchError
- type Vector
- func NewArrayVector(length int, elemType TypeID) *Vector
- func NewColumnVector(col parquet.Column, numRows int) *Vector
- func NewMapVector(length int, keyType, valueType TypeID) *Vector
- func NewRowVector(length int, fieldNames []string, fieldTypes []TypeID) *Vector
- func NewVector(typ TypeID, length int) *Vector
- func NewVectorLike(src *Vector) *Vector
- func NewVectorVector(length, dim int) *Vector
- func NewVectorWithScale(typ TypeID, length int, scale int) *Vector
- func NewViewVector(base *Vector, indices []uint32) *Vector
- func NewViewVectorReuse(base *Vector, indices, composeBuf []uint32) (*Vector, []uint32)
- func (v *Vector) AppendFrom(src *Vector, si int)
- func (v *Vector) Claim()
- func (v *Vector) Claimed() bool
- func (v *Vector) CopyValueFrom(di int, src *Vector, si int)
- func (v *Vector) EnsureLen(n int)
- func (v *Vector) Flatten()
- func (v *Vector) GetBool(i int) (bool, bool)
- func (v *Vector) GetFloat32(i int) (float32, bool)
- func (v *Vector) GetFloat64(i int) (float64, bool)
- func (v *Vector) GetInt32(i int) (int32, bool)
- func (v *Vector) GetInt64(i int) (int64, bool)
- func (v *Vector) GetNumericFloat64(i int) (float64, bool)
- func (v *Vector) GetString(i int) (string, bool)
- func (v *Vector) GetValue(i int) any
- func (v *Vector) IsView() bool
- func (v *Vector) MemBytes() int64
- func (v *Vector) ResetForWrite(n int)
- func (v *Vector) SetValue(i int, val any)
- func (v *Vector) SetVector(i int, vals []float32)
- func (v *Vector) String() string
- func (v *Vector) VectorAt(i int) []float32
- func (v *Vector) WriteNullAt(di int)
Constants ¶
const ( TypeBool = parquet.TypeBool TypeInt32 = parquet.TypeInt32 TypeInt64 = parquet.TypeInt64 TypeFloat32 = parquet.TypeFloat32 TypeFloat64 = parquet.TypeFloat64 TypeString = parquet.TypeString TypeBytes = parquet.TypeBytes TypeTimestamp = parquet.TypeTimestamp TypeIPv4 = parquet.TypeIPv4 TypeIPv6 = parquet.TypeIPv6 TypeCIDR = parquet.TypeCIDR TypeMAC = parquet.TypeMAC TypePort = parquet.TypePort TypeProtocol = parquet.TypeProtocol TypeDuration = parquet.TypeDuration TypeUUID = parquet.TypeUUID TypeDate = parquet.TypeDate TypeDecimal = parquet.TypeDecimal TypeArray = parquet.TypeArray TypeRow = parquet.TypeRow TypeMap = parquet.TypeMap TypeVector = parquet.TypeVector )
const DefaultBatchSize = 2048
DefaultBatchSize is the number of rows per batch (2048 for cache-friendly vectorized processing).
const ReservoirOwner uint64 = 1
ReservoirOwner is the sentinel ownerID stamped onto every batch minted by a BatchPool (Get, GetForSize, PreWarm). The zero value (ownerID == 0) means the batch is not pool-owned — e.g. the over-size escape hatch in GetForSize or a Detach'd long-lived batch. A non-zero sentinel keeps the zero value unambiguous, matching the Sel==nil / pool==nil "absent" conventions.
Variables ¶
This section is empty.
Functions ¶
func DecodeContainerColumn ¶
DecodeContainerColumn reads a payload written by EncodeContainerColumn into v, which must already have v.Type set (the WSHF schema's type byte) but need not carry any nested structure: child types, ROW field names and the VECTOR dimension all ride in the payload. The payload must be consumed exactly — trailing bytes are a corruption, not slack.
func EncodeContainerColumn ¶
EncodeContainerColumn appends the payload for rows [0, n) of v to dst and returns the grown slice. v must be a canonical vector: no view indirection, storage exactly n rows wide, ARRAY/MAP offsets starting at 0. (*Vector).NewVectorLike + AppendFrom produces exactly that from any source, which is how the WSHF writer feeds this.
func FormatDate ¶
FormatDate formats days-since-epoch as "2006-01-02".
func FormatTimestamp ¶
FormatTimestamp renders epoch MILLISECONDS — the engine's one timestamp unit — the way PostgreSQL renders a `timestamp` (OID 1114): UTC, "2006-01-02 15:04:05", with a fractional part only when the millisecond component is non-zero.
This is the display half of a deliberate split. The COMPUTE half boxes a TIMESTAMP column as a bare int64 (ColRef.Eval, pinned by TestTemporalColumnBoxingUnchanged) because comparison, arithmetic, GROUP BY key serialization, spill codecs and the UPDATE read-modify-write path all read it as a number; Vector.GetValue keeps that same int64 for exactly those consumers. The two halves agree because they are the same value in the same unit — one rendered, one raw — and the conversion between them lives here, applied by renderers that still hold the column's declared type. Formatting inside GetValue instead would push the rendered form into every compute path that shares that boxing.
func FormatValue ¶
FormatValue formats any value for display, producing SQL-like text for nested types (arrays, rows, maps).
func IsContainerType ¶
IsContainerType reports whether t's WSHF payload is encoded by this codec rather than by a flat per-type arm.
func NewMintOwner ¶
func NewMintOwner() uint64
NewMintOwner returns a process-unique producer id for MintStamp.Owner.
func PoisonOnRelease ¶
func PoisonOnRelease() bool
PoisonOnRelease reports whether poison-on-release is armed.
func PoisonedBatches ¶
func PoisonedBatches() uint64
PoisonedBatches returns the running count of batches poisoned on release.
func SetPoisonOnRelease ¶
SetPoisonOnRelease turns poison-on-release on or off and returns the previous setting, so a caller can restore it with a defer.
It is process-global and affects every pool in the process. Callers that flip it must not run concurrently with unrelated queries whose answers they care about — a gate opens it around one query at a time.
func SyncContainerSchema ¶
func SyncContainerSchema(b *RecordBatch)
SyncContainerSchema copies the shape a container column's PAYLOAD just revealed back into the batch's schema — VECTOR dimension, ARRAY/MAP element type, ROW field list.
The WSHF schema header records a name and a type byte and nothing else, so a decoded batch's schema says "VECTOR" with dimension 0 and "ROW" with no fields. The vectors themselves come out right (the payload carries the shape), but downstream operators size their OWN output from the schema of the batch they were handed: exec.Sort takes s.schema = b.Schema on its first Consume and then gathers into a vector built from it, which for a dimension-0 VECTOR is a nil Float32Data and a slice-bounds panic in gatherSortVector. That is #397's second face — the receiving operator is handed a shapeless container — and the payload is the authority to fix it with, since it is derived from the data rather than from a plan's guess.
The batch gets its OWN schema slice when there is anything to patch: the decode-ahead reader decodes chunks CONCURRENTLY off one shared schema, so patching in place would be a data race even though every writer would store the same value.
Types ¶
type BatchPool ¶
type BatchPool struct {
// contains filtered or unexported fields
}
BatchPool manages reusable RecordBatch allocations with size-class bucketing. Batches are pooled by their schema and row count to avoid allocation on the hot path. Thread-safe for concurrent operator use.
func NewBatchPool ¶
NewBatchPool creates a pool for batches of the given schema and size.
func (*BatchPool) Get ¶
func (p *BatchPool) Get() *RecordBatch
Get returns a batch from the pool, or allocates a new one.
func (*BatchPool) GetForSize ¶
func (p *BatchPool) GetForSize(numRows int) *RecordBatch
GetForSize returns a batch from the pool reset for the given numRows. If numRows exceeds the pool's batch size, allocates a fresh batch.
func (*BatchPool) PreWarm ¶
PreWarm pre-allocates n batches into the pool. Call before parallel workers start to avoid allocation contention during the scan hot path.
func (*BatchPool) Put ¶
func (p *BatchPool) Put(b *RecordBatch)
Put returns a batch to the pool for reuse.
type Bitmap ¶
type Bitmap struct {
// contains filtered or unexported fields
}
Bitmap is a compact null bitmap using 1 bit per row.
func NewBitmap ¶
NewBitmap creates a new bitmap with the given capacity, all bits set to 1 (non-null).
func NewBitmapAllNull ¶
NewBitmapAllNull creates a bitmap with all bits cleared (all null).
func (*Bitmap) CopyFrom ¶
CopyFrom copies the first n bits from src into b. Both bitmaps must have capacity for n bits. Uses word-level copy for the bulk and masks the final word.
func (*Bitmap) EnsureLen ¶
EnsureLen grows the bitmap to at least n bits, defaulting any newly added bits to non-null (1) to match NewBitmap. Existing bits are preserved. Used by append-style builders that grow a column across many source batches instead of pre-sizing to a worst-case capacity.
func (Bitmap) Grow ¶
Grow returns a bitmap that can hold at least newLen bits, preserving existing data. If the current bitmap is already large enough, it is returned as-is.
All bits in the newly-exposed range [b.len, newLen) are set to 1 (valid). This includes the previously-excess bits of the OLD last word: NewBitmap zeros those as padding, so once Grow brings them into the valid range we must explicitly mark them valid — otherwise they read back as null and silently drop rows from downstream consumers (e.g., HashAggregate routes "null GROUP BY key" rows to strGroupStates while int-keyed Next() emits only intGroupStates, dropping the rows from output).
func (*Bitmap) HasNulls ¶
HasNulls returns true if any bit is 0 (null). Short-circuits on the first non-full word, making it O(1) in the common all-valid case. Result is cached — repeated calls are free.
func (*Bitmap) InvalidateCache ¶
func (b *Bitmap) InvalidateCache()
InvalidateCache forces the next HasNulls() call to rescan the bitmap data. Must be called after modifying bitmap words directly via Words().
func (*Bitmap) IsNull ¶
IsNull returns true if the bit at position i is 0 (null). Includes bounds checking for safety at API boundaries.
func (*Bitmap) IsNullFast ¶
IsNullFast returns true if the bit at position i is 0 (null). No bounds checking — caller must ensure 0 <= i < b.len. Use in hot loops where the index is known to be valid.
func (*Bitmap) ResetNonNull ¶
ResetNonNull resets the bitmap to all non-null (all bits 1) for n elements, reusing the existing backing slice when capacity allows. This avoids allocation on the batch pool hot path.
func (*Bitmap) SetNullRange ¶
SetNullRange sets bits [start, start+count) to 0 (null) using word-level operations. For runs spanning full 64-bit words, entire words are zeroed in a single assignment instead of 64 individual bit clears.
type BytesColumn ¶
type BytesColumn struct {
Offsets []uint32 // len = num_rows + 1
Data []byte // contiguous buffer
// ShapeOnly marks a column decoded for its SHAPE only: Offsets carry
// the real per-row byte lengths but Data was never written (the
// lengths-only scan decode, internal/engine/scan/lengths_decode.go).
// LENGTH()/octet_length(), IS [NOT] NULL and the empty-string
// comparisons answer off Offsets and the null mask alone; any attempt
// to read a VALUE is a planner-analysis bug and panics immediately
// rather than returning a wrong answer. Copy paths propagate the flag
// instead of moving bytes that do not exist.
ShapeOnly bool
}
BytesColumn stores variable-length byte data (strings, binary) with zero per-row allocations using an offset/data layout.
func NewBytesColumn ¶
func NewBytesColumn(capacity int) BytesColumn
NewBytesColumn creates a new BytesColumn with the given capacity. Pre-allocates offsets for positional access (all offsets start at 0 = empty strings). Data arena is lazily allocated: starts empty and grows on first use. Hot paths (scan BulkSet, gather PreAllocBytes) know the exact size they need, so eager pre-allocation just wastes memclr on unused capacity. For pooled batches, the grown capacity is retained across Reset cycles.
func (*BytesColumn) BulkCopy ¶
func (dst *BytesColumn) BulkCopy(dstOff int, src *BytesColumn, srcOff, count int)
BulkCopy copies a contiguous range [srcOff, srcOff+count) from src into dst at [dstOff, dstOff+count). Uses a single Data append + offset arithmetic instead of per-element Set calls, reducing memmove overhead for batch merging.
func (*BytesColumn) BulkSet ¶
func (bc *BytesColumn) BulkSet(dstOffset int, srcData []byte, srcOffsets []uint32, n int)
BulkSet copies a contiguous block of byte array data into the column, computing offsets from the source offset array. This replaces n individual Set calls with a single bulk append + offset arithmetic, reducing memmove overhead for Parquet page loading.
func (*BytesColumn) LengthAt ¶
func (bc *BytesColumn) LengthAt(i int) int
LengthAt returns the byte length of row i without reading the value. It is the only value-shaped accessor valid on a shape-only column, and it mirrors Value's defensive handling of the descending-offset hazard.
func (*BytesColumn) MemBytes ¶
func (bc *BytesColumn) MemBytes() int64
MemBytes returns the heap bytes consumed by the offset and data slices.
Offsets are sized by len (the logical rows+1), but the data arena is sized by cap: a pooled BytesColumn retains its grown arena capacity across Reset cycles (see NewBytesColumn), so cap(Data) is the true resident footprint. This is the honest byte count that replaces the b.Len*48 estimate in EstimateBatchBytes.
func (*BytesColumn) PreAllocBytes ¶
func (bc *BytesColumn) PreAllocBytes(n int)
PreAllocBytes ensures the Data arena has at least n bytes of capacity. Use this when the expected total byte size is known (e.g., from Parquet metadata) to avoid reallocations during sequential Set calls.
func (*BytesColumn) ResetForWrite ¶
func (bc *BytesColumn) ResetForWrite(n int)
ResetForWrite resizes the column to hold exactly n values and clears it for a fresh sequential write, retaining the data arena's capacity. Callers that know the total byte size still call PreAllocBytes afterwards; once the arena has reached its high-water mark that call becomes a no-op instead of a fresh multi-hundred-KB span. See (*Vector).ResetForWrite.
func (*BytesColumn) Set ¶
func (bc *BytesColumn) Set(i int, val []byte)
Set writes a value at positional index i. The BytesColumn must have been created with NewBytesColumn(capacity >= i+1). Values must be set in order (i = 0, 1, 2, ...) because later offsets depend on prior data length.
func (*BytesColumn) SetFrom ¶
func (dst *BytesColumn) SetFrom(di int, src *BytesColumn, si int)
SetFrom copies a single value from src at position si into dst at position di. Combines Value + Set into one call, avoiding the intermediate slice creation and reducing function call overhead in gather loops. Values must be set in order (di = 0, 1, 2, ...) because later offsets depend on prior data length.
func (*BytesColumn) SetString ¶
func (bc *BytesColumn) SetString(i int, val string)
SetString writes a string value at positional index i. Same contract as Set (sequential i), but takes a string: `append(dst, s...)` copies straight out of the string, where Set's callers had to materialize a []byte(s) conversion first — one heap allocation per row on the string-producing projection paths.
func (*BytesColumn) StringValue ¶
func (bc *BytesColumn) StringValue(i int) string
StringValue returns the string at position i.
func (*BytesColumn) UnsafeStringValue ¶
func (bc *BytesColumn) UnsafeStringValue(i int) string
UnsafeStringValue returns a zero-copy string view of the value at position i. The returned string shares the BytesColumn's backing buffer and is only valid while the BytesColumn is not modified or recycled. Use for transient comparisons in filter/sort kernels where the string is consumed immediately.
func (*BytesColumn) Value ¶
func (bc *BytesColumn) Value(i int) []byte
Value returns the byte slice at position i.
Defensive against a gather-output hazard: when HashJoin's gatherBuildVector skips unmatched rows without calling BytesData.SetFrom, the destination Offsets may end up with Offsets[i+1] == 0 while Offsets[i] > 0, producing a malformed descending pair. Treat this as empty rather than panicking; the null bitmap alongside the column records the "no value" state authoritatively, and downstream filter / projection kernels already consult it.
type DecimalColumn ¶
DecimalColumn stores an array of Int128 values for DECIMAL vectors.
func NewDecimalColumn ¶
func NewDecimalColumn(capacity, scale int) DecimalColumn
NewDecimalColumn creates a new decimal column with the given capacity and scale.
type GlobalPool ¶
type GlobalPool struct {
// contains filtered or unexported fields
}
GlobalPool provides shared batch pooling across operators with the same schema. This avoids each operator maintaining its own pool and improves reuse when multiple operators in a pipeline share a schema.
type Int128 ¶
Int128 is a 128-bit signed integer used for DECIMAL storage. Values are stored as scaled integers: DECIMAL(10,2) value 123.45 → 12345.
func Int128FromFloat64 ¶
Int128FromFloat64 converts a float64 to Int128 with the given scale. For example, Int128FromFloat64(123.45, 2) → Int128 representing 12345.
func ParseDecimalString ¶
ParseDecimalString parses a decimal string like "123.45" into an Int128 with the given scale.
func (Int128) BigInt ¶
BigInt returns the value as a big.Int: Hi x 2^64 + Lo, exactly. Used on the paths where an Int128 is too narrow to hold an intermediate result.
func (Int128) FormatDecimal ¶
FormatDecimal formats the Int128 as a decimal string with the given scale.
func (Int128) IsNegative ¶
IsNegative returns true if the value is negative.
func (Int128) MulPow10 ¶
MulPow10 returns d x 10^n and reports whether the EXACT product fits in Int128. It never returns an approximation: a false second result means the caller must take a wider path, not that the first result is close.
Rescaling one operand up to the other's scale is how two DECIMALs of different scale are compared exactly (kernel.CompareDecimalAt), which matters at a sort-merge join key where the comparator decides EQUALITY: float64 rescaling makes 9007199254740993 and 9007199254740992.0 the same number and emits a join row for a pair that does not match.
type MintStamp ¶
MintStamp records WHICH producer minted a batch's storage and WHICH issue of that storage this is. It exists so a producer that hands storage out and takes it back — today the scan row-group backing pool, docs/design/scan-output-backing-reuse.md — can recognize its own batch at the release edge WITHOUT keeping a reference to it.
A registry of outstanding batches keyed by pointer is the obvious implementation and the wrong one: it is a strong reference the GC cannot collect and the memory ledger cannot see. A consumer with no release edge, or a batch the pipeline simply drops, pins whole decoded row groups (~280 MB each at SF100) for the producer's lifetime, and any bound on the registry's SIZE silently turns reuse off instead. The stamp inverts the direction: the batch points at nothing, the producer holds nothing, and identity survives in a pair of integers the batch carries.
Owner is a process-unique producer id from NewMintOwner. Zero means unstamped — what a WSHF shuffle chunk, a row-based fallback batch or any batch from a different producer carries — and a release edge must treat it as foreign, because adopting it would create a second owner for storage somebody else recycles.
Seq is bumped on every re-issue of the SAME storage. A release names the Seq it was handed, so a stale release from a previous generation (a retire that fired twice around a re-mint) names an older Seq and is ignored: re-admitting a LIVE backing to the free list would give two decoders one buffer, the one failure this design must not have.
The stamp is written by the producer while it owns the batch exclusively and read at the release edge; the producer's own publication edge (the decode ring's channel, the dispenser's channel send) and the pool mutex order every access.
type RecordBatch ¶
type RecordBatch struct {
Columns []*Vector
Schema []parquet.Column
Len int
Sel []uint32 // selection vector: indices of active rows (nil = all rows active)
// contains filtered or unexported fields
}
RecordBatch is the unit of data flowing between operators.
func FromRows ¶
func FromRows(schema []parquet.Column, rows []map[string]any) *RecordBatch
FromRows creates a RecordBatch from row-oriented data.
This is the ROW→BATCH boundary, and the one place where the two shapes of a MAP meet: the parquet row reader produces a Go map (and the writer consumes one) while the vector stores MAP as ARRAY(ROW("key","value")). Nothing converted between them, so every scan that fell back to the row reader — every query on a table carrying a nested column — handed Vector.SetValue a map it rejects and died on the scan worker (#393).
The conversion belongs HERE rather than in SetValue's MAP arm. SetValue cannot tell a MAP's row shape from a ROW's box (both are map[string]any), so accepting one there would silently reshape a ROW written into a mis-derived MAP vector — a live defect on the stage DAG (#397) that the #361 guard is currently the only thing reporting. At this boundary the context is unambiguous: the value came from the row reader and the catalog says the column is a MAP.
func NewRecordBatch ¶
func NewRecordBatch(schema []parquet.Column, numRows int) *RecordBatch
NewRecordBatch creates a new record batch with the given schema and row count.
func (*RecordBatch) ActiveLen ¶
func (b *RecordBatch) ActiveLen() int
ActiveLen returns the number of active rows (respecting selection vector).
func (*RecordBatch) ColumnByName ¶
func (b *RecordBatch) ColumnByName(name string) *Vector
ColumnByName returns the vector for the named column, or nil if not found.
func (*RecordBatch) ColumnIndex ¶
func (b *RecordBatch) ColumnIndex(name string) int
ColumnIndex returns the index of the named column, or -1.
func (*RecordBatch) Compact ¶
func (b *RecordBatch) Compact() *RecordBatch
Compact materializes the selection vector into a contiguous batch using the typed nested-aware value copier. Returns the batch unchanged when no selection vector is set.
Was previously a hand-rolled per-type switch whose ROW case wrote null child rows via SetNull alone — never advancing a string child's offset slot — so every later row in that child read back as concatenated garbage (same bug class as the windowCopyVectorRange nullable-BYTES fix; regression test TestCompact_RowChildNullableString).
func (*RecordBatch) Detach ¶
func (b *RecordBatch) Detach()
Detach claims ownership of the batch: Release() becomes a no-op so no pool can recycle it, and the claim is recorded on the batch AND on every column vector so a producer that reuses vector backing across calls surrenders it (see (*Vector).Claim). Call it from anything that keeps a batch — or anything pointing into its column storage — past the call that handed it over: the hash-join build, Sort, Window, the collect sinks, the spillable collector, partitioned aggregation's per-partition views.
The per-column claim is what makes the contract hold through a derived batch: ColumnPrune and the set-op emitter mint a NEW RecordBatch over the same *Vector pointers, so a consumer that detaches the derived batch would otherwise leave the producer of the original believing nobody kept it.
func (*RecordBatch) DetachPool ¶
func (b *RecordBatch) DetachPool()
DetachPool severs only the pool link, WITHOUT claiming the batch or its columns. It is for the one caller whose reference is transitive rather than independent: the hash-join late-materialization emitter, whose output views read the input's vectors, so the input must not be recycled underneath them by a concurrently-running source — but whose views die with its own output batch, whose consumer's Detach (if any) propagates the claim through Vector.Base anyway. Anything that genuinely KEEPS a batch calls Detach.
func (*RecordBatch) EnsureCapacity ¶
func (b *RecordBatch) EnsureCapacity(n int)
EnsureCapacity grows every column so positions [0, n) are addressable for in-place writes, preserving existing data, and sets Len to n. Used by append-style builders (e.g. the hash-join per-partition accumulator) that grow a batch across many source batches rather than pre-sizing to a worst-case capacity. See Vector.EnsureLen for per-type behavior and the nested-type caveat.
func (*RecordBatch) FlattenColumn ¶
func (b *RecordBatch) FlattenColumn(i int)
FlattenColumn materializes a single column if it is a view. Use for column-granular consumers (a filter touching one column) so the remaining columns stay lazy.
func (*RecordBatch) FlattenViews ¶
func (b *RecordBatch) FlattenViews()
FlattenViews materializes every view column in place. Call before retaining a batch past the pipeline's per-batch cycle (Sort/Window/sink Consume) or handing it to code that reads typed storage directly.
func (*RecordBatch) HasViews ¶
func (b *RecordBatch) HasViews() bool
HasViews reports whether any column of the batch is a view vector.
func (*RecordBatch) MemBytes ¶
func (b *RecordBatch) MemBytes() int64
MemBytes returns the in-memory byte footprint of the batch's column data, summing each Vector's MemBytes(). It deliberately omits operator-specific overhead (e.g. the HashJoin hash-index charge) — that stays at the call site (see hashBuildBytes in package exec). Replaces the per-type estimate that lived in exec.EstimateBatchBytes.
func (*RecordBatch) Mint ¶
func (b *RecordBatch) Mint() MintStamp
Mint returns the producer stamp on this batch (zero Owner = unstamped).
func (*RecordBatch) Release ¶
func (b *RecordBatch) Release()
Release returns the batch to its pool if applicable.
Once released, the batch's storage is undefined: the pool may hand it to another operator, which resets it and writes over the same arenas. Anything keeping a value out of the batch past this point must own it — see Detach. Poison mode (see poison.go) makes that undefinedness observable by scribbling the arenas here, which is what the batch-reuse gate compares against a clean run.
func (*RecordBatch) Reset ¶
func (b *RecordBatch) Reset(numRows int)
Reset clears the batch for reuse, keeping allocated memory.
func (*RecordBatch) Retained ¶
func (b *RecordBatch) Retained() bool
Retained reports whether a consumer claimed ownership of this batch with Detach.
func (*RecordBatch) RowAt ¶
func (b *RecordBatch) RowAt(i int) map[string]any
RowAt boxes a single physical row (Sel is not consulted) as a map. For callers that need a few rows out of a large batch — boxing the whole batch via ToRows for a low-selectivity pick is the documented multi-GB heap pattern.
func (*RecordBatch) SetMint ¶
func (b *RecordBatch) SetMint(m MintStamp)
SetMint stamps the batch for its producing free list. Only the producer calls it, and only while it owns the batch exclusively — at mint, and again to clear the stamp when the storage is taken back. The stamp travels with a VALUE copy of the RecordBatch (e.g. `nb := *b`), so any such copy taken over a scan batch must zero the stamp (`nb.SetMint(batch.MintStamp{})`) or it would alias the parent's pool identity; today the only value copy (internal/engine/exec/partitioned_agg.go's selView) Detaches immediately, which claims the shared columns and trips the release veto regardless.
func (*RecordBatch) ToRows ¶
func (b *RecordBatch) ToRows() []map[string]any
ToRows converts a RecordBatch to row-oriented data.
type TypeMismatchError ¶
type TypeMismatchError struct {
Dst TypeID // the vector's type
Val any // the value that had nowhere to go
}
TypeMismatchError reports a write of a value a vector has nowhere to put: SetValue was handed a Go value whose type has no conversion into the vector's storage.
Until #361 such a write VANISHED — the slot kept its zero value and was marked valid — which is the mechanism behind an entire bug family (#310, #327, #331, #333, #345, #353, #361, #371, #372): some declaration upstream picks the wrong vector type, and instead of an error the query answers 0 on every row. The write site is the one seam every one of those defects must cross, so it now panics with this typed value.
The panic carries a query ERROR, not a crash: it implements the exec.FatalEvalPanic contract (Error + FatalEvalError), the same route the expression evaluator uses for a condition with no error return (#347). The pipeline drivers, the worker's task-level recover, the coordinator's and the embedded API's query entries all convert it back into an error — "a wrong type may cost a wrong answer, never the server" (#310) still holds, with the improvement that it now costs an ERROR instead of a wrong answer.
The deliberate non-panics: a nil value is a NULL (WriteNullAt); STRING and BYTES destinations coerce any value through its string form, which is a documented rendering (group keys rely on it); and a PARSE failure of a value-level string (an unparseable IPv4, MAC, UUID) keeps its historical null-ish result — the type was right, the value was not.
func (*TypeMismatchError) Error ¶
func (e *TypeMismatchError) Error() string
func (*TypeMismatchError) FatalEvalError ¶
func (e *TypeMismatchError) FatalEvalError() error
FatalEvalError implements the exec.FatalEvalPanic contract, so pipeline drivers convert the panic into a query error instead of a process exit.
type Vector ¶
type Vector struct {
Type TypeID
Len int
Nulls Bitmap
BoolData []bool
Int32Data []int32
Int64Data []int64
Float32Data []float32
Float64Data []float64
BytesData BytesColumn
DecimalData DecimalColumn // for TypeDecimal
// VECTOR type: fixed-dimension float32 embeddings
// Row i's vector: Float32Data[i*VectorDim : (i+1)*VectorDim]
VectorDim int // VECTOR: dimensionality (number of float32 elements per row)
// Nested type fields (ARRAY, ROW, MAP)
Offsets []int32 // ARRAY/MAP: offsets[i]..offsets[i+1] delimit child elements for row i
Child *Vector // ARRAY: flat vector of all element values
Children []*Vector // ROW: one vector per field (same length as parent)
FieldNames []string // ROW: names of child fields
// View (dictionary) form: when Base != nil this vector owns no typed
// storage — logical row i is Base row Indices[i]. Nulls is the view's OWN
// override bitmap (a null bit marks the row null regardless of Base; a
// valid bit defers to Base's nullness through Indices). Base is always an
// owned vector: NewViewVector composes indices when handed a view base, so
// views never chain. Views are read-only and understood only by the
// view-aware accessors (GetValue, CopyValueFrom-as-source, Flatten,
// MemBytes); typed hot-path accessors (GetInt64, Int64Data[i], ...) fail
// loud on a view because the typed slices are nil. See view.go.
Base *Vector
Indices []uint32
// contains filtered or unexported fields
}
Vector holds a single column of data. Uses typed slices instead of interface{}.
func NewArrayVector ¶
NewArrayVector creates a new ARRAY vector with the given length and element type. The child vector starts with capacity 0; callers append elements and update offsets.
func NewColumnVector ¶
NewColumnVector creates a single Vector from a Column definition with numRows pre-allocated rows, recursively initializing nested type children — the per-column equivalent of NewRecordBatch for callers that materialize some columns of a batch while emitting others as views.
func NewMapVector ¶
NewMapVector creates a new MAP vector. Internally stored as ARRAY(ROW("key","value")).
func NewRowVector ¶
NewRowVector creates a new ROW/STRUCT vector with named child fields.
func NewVectorLike ¶
NewVectorLike returns an empty (zero-row) vector with src's type and nested structure: child element types, ROW field names, VECTOR dim and DECIMAL scale. Element storage is appended by AppendFrom.
func NewVectorVector ¶
NewVectorVector creates a new VECTOR column with fixed dimensionality. Storage: Float32Data of length * dim, where row i occupies [i*dim, (i+1)*dim).
func NewVectorWithScale ¶
NewVectorWithScale creates a new vector with scale metadata (used for DECIMAL).
func NewViewVector ¶
NewViewVector creates a view over base addressing rows through indices. The indices slice is adopted, not copied — callers must not mutate it afterwards. If base is itself a view, the indirection is composed away (newIndices[i] = base.Indices[indices[i]], own-nulls folded), so Base on the returned vector is always an owned vector.
The view starts all-valid: row i's nullness defers to base through indices. Callers that need null-injection (outer-join fill) mark rows with v.Nulls.SetNull(i); those rows' index values are ignored.
func NewViewVectorReuse ¶
NewViewVectorReuse is NewViewVector with a caller-owned composition buffer.
When base is itself a view its indirection has to be folded into a NEW index array, and at join-emit widths that array is a large-object allocation per column per output batch — one of the spans the Go heap lock serializes on. Passing composeBuf lets a caller that owns the resulting view's lifetime keep that array across batches. The returned slice is the array the view ADOPTED: it stays live for as long as the view does, so only a caller that knows the view is dead may re-pass it (see probeEmitBuf's ownership rule in package exec). nil comes back when no composition was needed and `indices` was adopted directly.
func (*Vector) AppendFrom ¶
AppendFrom appends src[si] to dst, growing dst by one row. Typed copy — no boxing, no string round-trips — recursive for nested types. dst's nested structure must match src's (build it with NewVectorLike).
func (*Vector) Claim ¶
func (v *Vector) Claim()
Claim marks this vector's storage as retained by a consumer, recursively through the view base it reads from and its nested children. Once claimed a vector is never reused by its producer: the claim is sticky because nothing tracks when the retaining consumer is finished (a Sort holds its input until Finalize), and a wrong answer here is silent data corruption.
func (*Vector) CopyValueFrom ¶
CopyValueFrom writes src[si] into position di of dst using typed access — no boxing, no string round-trips — for every column type including nested ARRAY/MAP/ROW. Fixed-width slots are indexed; variable-length storage (bytes data, array child elements, lazily-created row children) is appended, so writes must be SEQUENTIAL per column (di = 0, 1, 2, ...) — the same contract BytesColumn.Set has always had. Null source rows still advance offsets and children; skipping them would shift every later row.
Destination shape is flexible per level: parent slots may be pre-allocated (NewRecordBatch with full nested schema) or append-built (NewVectorLike); ROW children handle both — indexed writes when pre-allocated, appends when built lazily.
func (*Vector) EnsureLen ¶
EnsureLen grows the vector's backing storage so positions [0, n) are addressable for in-place writes (Set / index-assign), preserving existing values, defaulting new fixed-width slots to zero and new null bits to non-null. Backing arrays grow geometrically (via append) for amortized O(1) appends, so an append-style builder can grow a column across many source batches instead of pre-sizing to a worst-case capacity. Sets Len to n.
Scalar, bytes, decimal and fixed-dim VECTOR columns are fully supported. Nested ARRAY/MAP element storage and ROW children are NOT grown here (their element storage is appended by SetValue); callers that build nested columns should use pre-sized batches. The hash-join accumulator path that relies on EnsureLen guards nested schemas to a pre-sized path for this reason.
func (*Vector) Flatten ¶
func (v *Vector) Flatten()
Flatten materializes a view in place: owned storage is allocated, values are gathered from Base through Indices (own-null rows become nulls), and the view fields are cleared. Aliases of the *Vector see the flattened form. No-op on owned vectors.
func (*Vector) GetBool ¶
GetBool returns the bool value at position i. Returns (false, false) if null.
func (*Vector) GetFloat32 ¶
GetFloat32 returns the float32 value at position i. Returns (0, false) if null.
func (*Vector) GetFloat64 ¶
GetFloat64 returns the float64 value at position i. Returns (0, false) if null.
func (*Vector) GetInt32 ¶
GetInt32 returns the int32 value at position i. Returns (0, false) if null.
func (*Vector) GetInt64 ¶
GetInt64 returns the int64 value at position i. Returns (0, false) if null.
func (*Vector) GetNumericFloat64 ¶
GetNumericFloat64 returns any numeric column value as float64 without boxing. Handles Int32, Int64, Float32, Float64, Timestamp types.
func (*Vector) GetString ¶
GetString returns the string value at position i. Returns ("", false) if null.
func (*Vector) GetValue ¶
GetValue returns the value at position i as an interface{}. Note: returns boxed values for numeric types (unavoidable with any return type). Prefer typed accessors (GetInt64, GetFloat64, etc.) in hot paths.
func (*Vector) MemBytes ¶
MemBytes returns the heap bytes resident in this vector's backing storage: the null bitmap plus the typed data slice, recursing into nested children for ARRAY/MAP/ROW. It is the byte-true accounting primitive for the memory tracker, replacing the per-type b.Len*48 estimate in EstimateBatchBytes. It deliberately omits any operator-specific overhead (e.g. the HashJoin hash index charge) — that stays at the call site.
func (*Vector) ResetForWrite ¶
ResetForWrite resizes an OWNED vector to exactly n rows and clears its per-row state — null bits back to non-null, fixed-width slots to zero, the bytes arena emptied — while RETAINING every backing allocation's capacity. A producer that reuses one vector across output batches therefore allocates only when n passes its high-water mark, where a fresh NewColumnVector allocates (and the runtime zeroes) a new span every single batch.
Slots are cleared, not merely resized: the gather loops skip writing null and unmatched rows, so a stale value under a null bit would be a reuse-visible difference from the freshly-zeroed path for any reader that looks at a null slot's value. The clear costs the same memclr `make` was already paying; what is saved is the allocation, i.e. the Go heap lock.
Nested ARRAY/MAP/ROW element storage is append-built and is NOT reset here; callers must not reuse vectors of those types (the join emit path guards them out and mints fresh).
func (*Vector) SetValue ¶
SetValue sets the value at position i from an interface{}. For string/bytes types, values must be set in sequential order (i = 0, 1, 2, ...).
A non-nil value whose type has no conversion into this vector's storage PANICS with *TypeMismatchError (#361) instead of silently keeping the zero value — see that type's doc for the contract and the seams that convert the panic into a query error. nil is a NULL; STRING/BYTES coerce everything through its string form; a parseable-type string that fails to parse (IPv4, MAC, UUID) keeps its historical value-level behavior.
func (*Vector) WriteNullAt ¶
WriteNullAt writes a null into position di of a pre-allocated vector, advancing variable-length bookkeeping (bytes offsets, array offsets, row children) so later sequential writes stay aligned. This is THE null-write primitive for indexed sequential writers — any writer that sets the null bit without advancing these slots corrupts every later row in the column.