Execution

Vectorized, pull-based operator pipeline processing ~1024 rows per batch.

Core Abstractions

Vector

A typed array of up to 1024 values from a single column. The basic unit of computation — operators work on vectors, not individual values.

Batch

A set of aligned vectors (one per column) plus a Selection — an index list that tracks which rows are "live." All vectors share the same raw length; the selection narrows visible rows without copying data.

Selection (Late Materialization)

Instead of removing filtered rows from vectors, a selection vector records which indices survived. Downstream operators only process selected indices. This avoids expensive data movement.

// Before filter: selection = [0, 1, 2, 3, 4, 5]
// After  filter (age > 30): selection = [1, 3, 5]
// Vectors unchanged — only the index list shrinks

Operators

All operators implement the pull-based Operator interface:

type Operator interface {
    Next() (*Batch, bool)
    Reset()
}
OperatorWhat it does
ScanOpReads a RowGroup and yields batches of up to 1024 rows
FilterOpEvaluates a predicate and narrows the selection
ProjectOpKeeps only specified columns (by index, no copying)
AggregateOpRuns aggregators over the entire stream (no grouping)
GroupByOpSingle-key hash GROUP BY with fast paths for int64/string
OrderByOpSorts batches by a column
LimitOpStops after N rows

Predicates

Seven comparison predicates for filter evaluation:

=   !=   <   <=   >   >=

Each predicate is implemented per type (int64, float64, string, bool) as a tight loop over the selection vector.

Aggregators

AggregatorTypes
COUNT(*)All
COUNT(col)All (skips nulls)
SUMInt64, Float64
MINInt64, Float64
MAXInt64, Float64
AVGInt64, Float64

All 9 aggregators are zero-alloc at steady state — they resize internal state once per new group, not per row.

Benchmark Highlights

GROUP BY on 1M rows (Apple M4):

CardinalitySpeedup vs naiveAllocs
10 groups (string)0.79x13 → 0
10k groups (int64)1.67x10,033 → 0
100k groups (int64)1.44x100,252 → 6

The vectorized approach wins on high-cardinality data where allocation pressure matters. On low-cardinality, the overhead of batch processing slightly exceeds the gains.

Files

FileRole
operator.goOperator interface
batch.goBatch struct (vectors + selection)
vector.goTyped vector (up to 1024 values)
selection.goSelection index list
scan.goScanOp — RowGroup reader
filter.goFilterOp — predicate evaluation
project.goProjectOp — column projection
predicate.go7 comparison predicates
aggregate.goAggregateOp — single-group aggregation
aggregators.go9 concrete aggregator implementations
groupby.goGroupByOp — hash GROUP BY
orderby.goOrderByOp — sort
limit.goLimitOp — row limit