Documentation
¶
Overview ¶
Package exec implements the Volcano-style executor for the Cypher query engine. It defines the Operator interface, the Row/RowSlab data model, and the pipeline driver Drain.
Data model ¶
A Row is a slice of expr.Value. A RowSlab is a bounded, pooled container of pre-allocated rows used to eliminate per-row heap allocations in the hot path.
Concurrency ¶
RowSlab is NOT safe for concurrent use. Each goroutine must obtain its own slab from NewRowSlab or from a sync.Pool managed by the caller. The exported SlabPool provides a ready-to-use pool with default capacity.
Index ¶
- Constants
- Variables
- func BitSet(bitmap []uint64, i int) bool
- func EmitsExactly(op Operator, cols []string) bool
- func EnforceUniqueOnLabelRemove(reg *ConstraintRegistry, mutator GraphMutator, nodeKey, label string)
- func EnforceUniqueOnLabelSet(reg *ConstraintRegistry, mutator GraphMutator, mgr *index.Manager, ...) error
- func EnforceUniqueOnPropertyDelete(reg *ConstraintRegistry, mutator GraphMutator, nodeKey, key string)
- func EnforceUniqueOnPropertySet(reg *ConstraintRegistry, mutator GraphMutator, mgr *index.Manager, ...) error
- func ExpandIntoSeekCount() uint64
- func ExpandIntoSeekReverseCount() uint64
- func MergeActionEvalKey(targetVar, key string) string
- func NewUniqueBackingIndex() index.Subscriber
- func PropMapContainsNullLiteral(s string) bool
- func RenderPlan(op Operator) string
- func RenderPlanNode(n *PlanNode) string
- func UniqueIndexName(label, prop string) string
- func WithCyphermorphism(relCols []int) expandOption
- type AdjacencySource
- type AggInputFactory
- type AggReducerKind
- type AllNodesCountScan
- type AllNodesScan
- type AllShortestPaths
- func (op *AllShortestPaths) Close() error
- func (op *AllShortestPaths) Init(ctx context.Context) error
- func (op *AllShortestPaths) Next(out *Row) (bool, error)
- func (op *AllShortestPaths) PlanChildren() []Operator
- func (op *AllShortestPaths) WithHopBounds(minHops, maxHops int) *AllShortestPaths
- func (op *AllShortestPaths) WithOptional(optional bool) *AllShortestPaths
- func (op *AllShortestPaths) WithPathPredicate(pred func(Row) (bool, error)) *AllShortestPaths
- func (op *AllShortestPaths) WithTypeFilter(edgeType string) *AllShortestPaths
- func (op *AllShortestPaths) WithWorkBudget(maxPerRow, maxTotal int) *AllShortestPaths
- type AntiSemiApply
- type Apply
- type Argument
- type CSRAdjacency
- type Chunk
- func (c *Chunk) AppendBool(j int, v bool)
- func (c *Chunk) AppendFloat64(j int, v float64)
- func (c *Chunk) AppendInt64(j int, v int64)
- func (c *Chunk) AppendNull(j int)
- func (c *Chunk) AppendRowFrom(src *Chunk, srcRow int)
- func (c *Chunk) AppendString(j int, v string)
- func (c *Chunk) AppendValue(j int, v expr.Value)
- func (c *Chunk) Bool(j, row int) (value, valid bool)
- func (c *Chunk) BoolColumn(j int) (data []bool, valid []uint64, allValid bool)
- func (c *Chunk) BoxCell(j, row int) expr.Value
- func (c *Chunk) BoxRow(row int, dst Row) Row
- func (c *Chunk) Cap() int
- func (c *Chunk) ColKind(j int) expr.Kind
- func (c *Chunk) CopyCellTo(srcCol, srcRow int, dst *Chunk, dstCol int)
- func (c *Chunk) Float64(j, row int) (value float64, valid bool)
- func (c *Chunk) Float64Column(j int) (data []float64, valid []uint64, allValid bool)
- func (c *Chunk) Int64(j, row int) (value int64, valid bool)
- func (c *Chunk) Int64Column(j int) (data []int64, valid []uint64, allValid bool)
- func (c *Chunk) IsBoolColumn(j int) bool
- func (c *Chunk) IsFloat64Column(j int) bool
- func (c *Chunk) IsInt64Column(j int) bool
- func (c *Chunk) IsNull(j, row int) bool
- func (c *Chunk) IsStringColumn(j int) bool
- func (c *Chunk) IsValid(j, row int) bool
- func (c *Chunk) Len() int
- func (c *Chunk) NumCols() int
- func (c *Chunk) PutBool(j int, v bool)
- func (c *Chunk) PutFloat64(j int, v float64)
- func (c *Chunk) PutInt64(j int, v int64)
- func (c *Chunk) PutNull(j int)
- func (c *Chunk) PutString(j int, v string)
- func (c *Chunk) PutValue(j int, v expr.Value)
- func (c *Chunk) Reset()
- func (c *Chunk) RowByteEstimate(row int, overhead int64, estimateBoxed func(expr.Value) int64) int64
- func (c *Chunk) SetBool(j, row int, v bool)
- func (c *Chunk) SetFloat64(j, row int, v float64)
- func (c *Chunk) SetInt64(j, row int, v int64)
- func (c *Chunk) SetNull(j, row int)
- func (c *Chunk) SetString(j, row int, v string)
- func (c *Chunk) String(j, row int) (value string, valid bool)
- func (c *Chunk) StringColumn(j int) (data []string, valid []uint64, allValid bool)
- type ChunkColumnFiller
- type ChunkPool
- type ChunkPredicate
- type ChunkProducer
- type ColumnFiller
- type ColumnarFilter
- type ColumnarHashJoin
- func (op *ColumnarHashJoin) Close() error
- func (op *ColumnarHashJoin) FillChunk(dst *Chunk, maxRows int) (int, error)
- func (op *ColumnarHashJoin) Init(ctx context.Context) error
- func (op *ColumnarHashJoin) NewOutputChunk(capacity int) *Chunk
- func (op *ColumnarHashJoin) Next(out *Row) (bool, error)
- func (op *ColumnarHashJoin) PlanChildren() []Operator
- func (op *ColumnarHashJoin) PlanDetail() string
- func (op *ColumnarHashJoin) WithByteBudget(maxBytes int64, estimateRow func(Row) int64) *ColumnarHashJoin
- type ColumnarLimit
- type ColumnarProject
- func (op *ColumnarProject) FillChunk(dst *Chunk, maxRows int) (int, error)
- func (op *ColumnarProject) NewOutputChunk(capacity int) *Chunk
- func (op *ColumnarProject) WithChunkInput(chunkFillers []ChunkColumnFiller) error
- func (op *ColumnarProject) WithScalarChunkInput(chunkFillers []ChunkColumnFiller) error
- type ConstraintInfo
- type ConstraintKind
- type ConstraintRegistry
- func (r *ConstraintRegistry) CheckSetProperty(ct *ConstraintTxn, labels []string, prop string, value lpg.PropertyValue, ...) error
- func (r *ConstraintRegistry) CommitTxn(ct *ConstraintTxn)
- func (r *ConstraintRegistry) Constraints() []ConstraintInfo
- func (r *ConstraintRegistry) Count() int
- func (r *ConstraintRegistry) HasAnyNotNull() bool
- func (r *ConstraintRegistry) HasAnyUnique() bool
- func (r *ConstraintRegistry) HasNotNull(label, prop string) bool
- func (r *ConstraintRegistry) HasUnique(label, prop string) bool
- func (r *ConstraintRegistry) ListConstraintRows() [][]expr.Value
- func (r *ConstraintRegistry) NameInUse(name string) (kind ConstraintKind, label, prop string, found bool)
- func (r *ConstraintRegistry) NotNullProperties(label string) []string
- func (r *ConstraintRegistry) RecordPropertySet(labels []string, prop string, value lpg.PropertyValue)
- func (r *ConstraintRegistry) RegisterNotNull(label, prop string)
- func (r *ConstraintRegistry) RegisterUnique(label, prop, indexName string)
- func (r *ConstraintRegistry) ReleasePropertyValue(ct *ConstraintTxn, labels []string, prop string, value lpg.PropertyValue)
- func (r *ConstraintRegistry) ReseedFromGraph(scanFn func(label, prop string) []lpg.PropertyValue)
- func (r *ConstraintRegistry) ReserveSetProperty(ct *ConstraintTxn, labels []string, prop string, value lpg.PropertyValue, ...) error
- func (r *ConstraintRegistry) ResolveByName(name string) (kind ConstraintKind, label, prop string, found bool)
- func (r *ConstraintRegistry) SeedUniqueValues(label, prop string, values []lpg.PropertyValue) error
- func (r *ConstraintRegistry) SeedUniqueValuesIgnoringDuplicates(label, prop string, values []lpg.PropertyValue)
- func (r *ConstraintRegistry) SetConstraintName(kindUnique bool, label, prop, name string)
- func (r *ConstraintRegistry) UniqueIndexName(label, prop string) (string, bool)
- func (r *ConstraintRegistry) UniqueProperties(label string) []string
- func (r *ConstraintRegistry) UnregisterNotNull(label, prop string)
- func (r *ConstraintRegistry) UnregisterUnique(label, prop string)
- type ConstraintTxn
- type ConstraintViolationError
- type CorrelatedApply
- type CountBuffer
- type CreateConstraintOp
- type CreateIndexOp
- type CreateNode
- func (op *CreateNode) Close() error
- func (op *CreateNode) Init(ctx context.Context) error
- func (op *CreateNode) Next(out *Row) (bool, error)
- func (op *CreateNode) PlanChildren() []Operator
- func (op *CreateNode) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *CreateNode
- func (op *CreateNode) WithParams(params map[string]expr.Value) (*CreateNode, error)
- func (op *CreateNode) WithPropsEvalFn(fn PropsEvalFn) *CreateNode
- type CreateRelationship
- func (op *CreateRelationship) Close() error
- func (op *CreateRelationship) Init(ctx context.Context) error
- func (op *CreateRelationship) Next(out *Row) (bool, error)
- func (op *CreateRelationship) PlanChildren() []Operator
- func (op *CreateRelationship) WithParams(params map[string]expr.Value) (*CreateRelationship, error)
- func (op *CreateRelationship) WithPropsEvalFn(fn PropsEvalFn) *CreateRelationship
- type DeleteNode
- func (op *DeleteNode) Close() error
- func (op *DeleteNode) Init(ctx context.Context) error
- func (op *DeleteNode) Next(out *Row) (bool, error)
- func (op *DeleteNode) PlanChildren() []Operator
- func (op *DeleteNode) WithConstraintRegistry(reg *ConstraintRegistry) *DeleteNode
- func (op *DeleteNode) WithRelEndpoints(fn RelEndpointFn) *DeleteNode
- func (op *DeleteNode) WithTargetEvalFn(fn TargetEvalFn) *DeleteNode
- type DeleteRelationship
- type DetachDelete
- func (op *DetachDelete) Close() error
- func (op *DetachDelete) Init(ctx context.Context) error
- func (op *DetachDelete) Next(out *Row) (bool, error)
- func (op *DetachDelete) PlanChildren() []Operator
- func (op *DetachDelete) WithConstraintRegistry(reg *ConstraintRegistry) *DetachDelete
- func (op *DetachDelete) WithTargetEvalFn(fn TargetEvalFn) *DetachDelete
- type Direction
- type Distinct
- type DropConstraintOp
- type DropIndexOp
- type Eager
- type EagerAggregation
- func (op *EagerAggregation) Close() error
- func (op *EagerAggregation) Init(ctx context.Context) error
- func (op *EagerAggregation) Next(out *Row) (bool, error)
- func (op *EagerAggregation) PlanChildren() []Operator
- func (op *EagerAggregation) WithByteBudget(maxBytes int64, estimateRow func(Row) int64) *EagerAggregation
- func (op *EagerAggregation) WithChunkInput() error
- type EffectCountingSuppressor
- type Expand
- func (op *Expand) Close() error
- func (op *Expand) Init(ctx context.Context) error
- func (op *Expand) Next(out *Row) (bool, error)
- func (op *Expand) PlanChildren() []Operator
- func (op *Expand) PlanDetail() string
- func (op *Expand) WithExpandInto(col int) *Expand
- func (op *Expand) WithExpandIntoSeek(enabled bool) *Expand
- type ExpandConfig
- type ExpandIntersect
- type ExpandIntersectConfig
- type ExprValueEvalFn
- type Filter
- type FilterFn
- type Float64RangeIndex
- type Foreach
- type GlobalAggregateAdapter
- type GraphMutator
- type HashJoin
- func (op *HashJoin) Close() error
- func (op *HashJoin) Init(ctx context.Context) error
- func (op *HashJoin) Next(out *Row) (bool, error)
- func (op *HashJoin) PlanChildren() []Operator
- func (op *HashJoin) PlanDetail() string
- func (op *HashJoin) WithByteBudget(maxBytes int64, estimateRow func(Row) int64) *HashJoin
- type HashLookup
- type IndexBuffer
- type IndexKindExec
- type IndexNestedLoopJoin
- func (op *IndexNestedLoopJoin) Close() error
- func (op *IndexNestedLoopJoin) Init(ctx context.Context) error
- func (op *IndexNestedLoopJoin) Next(out *Row) (bool, error)
- func (op *IndexNestedLoopJoin) PlanChildren() []Operator
- func (op *IndexNestedLoopJoin) WithProvenNumericCoverage(proven bool) *IndexNestedLoopJoin
- type IndexRangePart
- type Int64HashIndex
- type Int64RangeIndex
- type IntersectAdjacencySource
- type KeyFn
- type LPGLabelSource
- type LabelCountScan
- type LabelIntersectResolver
- type Limit
- type MapEvalFn
- type Merge
- func (op *Merge) Close() error
- func (op *Merge) Init(ctx context.Context) error
- func (op *Merge) Next(out *Row) (bool, error)
- func (op *Merge) PlanChildren() []Operator
- func (op *Merge) WithActionEvals(onCreate, onMatch map[string]ValueEvalFn) *Merge
- func (op *Merge) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *Merge
- func (op *Merge) WithLabelSource(src MergeLabelSource) *Merge
- func (op *Merge) WithParams(params map[string]expr.Value) (*Merge, error)
- func (op *Merge) WithPropsEvalFn(fn PropsEvalFn) *Merge
- func (op *Merge) WithSetAllActions(onCreate, onMatch []MergeSetAllAction) *Merge
- type MergeLabelSource
- type MergePattern
- func (op *MergePattern) AddBoundNode(varName string, col int) *MergePattern
- func (op *MergePattern) AddFreshNode(varName string, labels []string, propsRaw string, outCol int) *MergePattern
- func (op *MergePattern) AddHop(relVar string, relCol int, relType, relPropsRaw string, ...) *MergePattern
- func (op *MergePattern) Close() error
- func (op *MergePattern) Init(ctx context.Context) error
- func (op *MergePattern) MarkHopRefsPatternNode() *MergePattern
- func (op *MergePattern) Next(out *Row) (bool, error)
- func (op *MergePattern) PlanChildren() []Operator
- func (op *MergePattern) WithActionEvals(onCreate, onMatch map[string]ValueEvalFn) *MergePattern
- func (op *MergePattern) WithActions(onCreateStrs, onMatchStrs []string) (*MergePattern, error)
- func (op *MergePattern) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *MergePattern
- func (op *MergePattern) WithHopPropsEvalFn(fn PropsEvalFn) *MergePattern
- func (op *MergePattern) WithLabelSource(src MergeLabelSource) *MergePattern
- func (op *MergePattern) WithNodePropsEvalFn(fn PropsEvalFn) *MergePattern
- func (op *MergePattern) WithParams(params map[string]expr.Value) (*MergePattern, error)
- func (op *MergePattern) WithSetAllActions(onCreate, onMatch []MergeSetAllAction) *MergePattern
- type MergeRelAction
- type MergeRelationship
- func (op *MergeRelationship) Close() error
- func (op *MergeRelationship) Init(ctx context.Context) error
- func (op *MergeRelationship) Next(out *Row) (bool, error)
- func (op *MergeRelationship) PlanChildren() []Operator
- func (op *MergeRelationship) WithActionEvals(onCreate, onMatch map[string]ValueEvalFn) *MergeRelationship
- func (op *MergeRelationship) WithOnCreate(relVar string, actions []MergeRelAction) *MergeRelationship
- func (op *MergeRelationship) WithOnMatch(relVar string, actions []MergeRelAction) *MergeRelationship
- func (op *MergeRelationship) WithParams(params map[string]expr.Value) (*MergeRelationship, error)
- func (op *MergeRelationship) WithRelColumn(relCol int) *MergeRelationship
- func (op *MergeRelationship) WithRelProperties(propsRaw string) *MergeRelationship
- func (op *MergeRelationship) WithRelPropsEvalFn(fn PropsEvalFn) *MergeRelationship
- func (op *MergeRelationship) WithSchema(schema map[string]int) *MergeRelationship
- func (op *MergeRelationship) WithUndirected(u bool) *MergeRelationship
- type MergeSearchFn
- type MergeSetAllAction
- type NodeByIndexRangeScan
- func (op *NodeByIndexRangeScan) Close() error
- func (op *NodeByIndexRangeScan) Init(ctx context.Context) error
- func (op *NodeByIndexRangeScan) Next(out *Row) (bool, error)
- func (op *NodeByIndexRangeScan) PlanDetail() string
- func (op *NodeByIndexRangeScan) RestrictToLabel(resolve func() *roaring64.Bitmap) *NodeByIndexRangeScan
- type NodeByIndexSeek
- type NodeByIndexSeekSet
- type NodeByLabelScan
- func (op *NodeByLabelScan) Close() error
- func (op *NodeByLabelScan) FillChunk(dst *Chunk, maxRows int) (int, error)
- func (op *NodeByLabelScan) Init(ctx context.Context) error
- func (op *NodeByLabelScan) NewOutputChunk(capacity int) *Chunk
- func (op *NodeByLabelScan) Next(out *Row) (bool, error)
- func (op *NodeByLabelScan) PlanDetail() string
- type NodeIDColumnProducer
- type NumericPointLookup
- type Operator
- type OptionalApply
- type OptionalExpand
- type ParallelAggregateScan
- func (op *ParallelAggregateScan) Close() error
- func (op *ParallelAggregateScan) Init(ctx context.Context) error
- func (op *ParallelAggregateScan) Next(out *Row) (bool, error)
- func (op *ParallelAggregateScan) WithByteBudget(maxBytes int64, estimateRow func(Row) int64) *ParallelAggregateScan
- func (op *ParallelAggregateScan) WithGroupCap(maxGroups int) *ParallelAggregateScan
- type ParallelCountScan
- type ParallelGovernor
- type ParallelScanProject
- type PlanChildren
- type PlanDetail
- type PlanNode
- type ProcedureCallOp
- type Profiler
- type Project
- func (op *Project) Close() error
- func (op *Project) Columns() []string
- func (op *Project) Init(ctx context.Context) error
- func (op *Project) Next(out *Row) (bool, error)
- func (op *Project) PlanChildren() []Operator
- func (op *Project) WithRowByteBudget(maxRowBytes int64, estimateValue func(expr.Value) int64) *Project
- type ProjectionItem
- type PropEntry
- type PropsEvalFn
- type QueryCounters
- type RangeBound
- type Record
- type RelCols
- type RelEndpointFn
- type RemoveLabels
- type RemoveProperty
- func (op *RemoveProperty) Close() error
- func (op *RemoveProperty) Init(ctx context.Context) error
- func (op *RemoveProperty) Next(out *Row) (bool, error)
- func (op *RemoveProperty) PlanChildren() []Operator
- func (op *RemoveProperty) WithConstraintRegistry(reg *ConstraintRegistry) *RemoveProperty
- func (op *RemoveProperty) WithRelCols(rc RelCols) *RemoveProperty
- type Result
- type ResultSet
- func (rs *ResultSet) Close() error
- func (rs *ResultSet) ColumnarProducer() (ChunkProducer, bool)
- func (rs *ResultSet) Columns() []string
- func (rs *ResultSet) Err() error
- func (rs *ResultSet) Next() bool
- func (op *ResultSet) PlanChildren() []Operator
- func (rs *ResultSet) Record() Record
- func (rs *ResultSet) Row() Row
- func (rs *ResultSet) RowCountHint() (n int, ok bool)
- func (rs *ResultSet) TakeRecord() Record
- type RollUpApply
- type Row
- type RowSlab
- type SemiApply
- type SetAllProperties
- func NewSetAllPropertiesFromEntity(entityVar, sourceVar string, isReplace bool, schema map[string]int, ...) *SetAllProperties
- func NewSetAllPropertiesFromExpr(entityVar string, isReplace bool, schema map[string]int, child Operator, ...) *SetAllProperties
- func NewSetAllPropertiesFromMap(entityVar, mapLiteral string, isReplace bool, schema map[string]int, ...) (*SetAllProperties, error)
- func NewSetAllPropertiesFromParam(entityVar, paramName string, isReplace bool, schema map[string]int, ...) *SetAllProperties
- func (op *SetAllProperties) Close() error
- func (op *SetAllProperties) Init(ctx context.Context) error
- func (op *SetAllProperties) Next(out *Row) (bool, error)
- func (op *SetAllProperties) PlanChildren() []Operator
- func (op *SetAllProperties) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *SetAllProperties
- func (op *SetAllProperties) WithExprEvalFn(fn ExprValueEvalFn) *SetAllProperties
- func (op *SetAllProperties) WithMapEvalFn(fn MapEvalFn) *SetAllProperties
- func (op *SetAllProperties) WithParams(params map[string]expr.Value) (*SetAllProperties, error)
- func (op *SetAllProperties) WithRelCols(rc RelCols) *SetAllProperties
- func (op *SetAllProperties) WithSourceRelCols(rc RelCols) *SetAllProperties
- type SetLabels
- type SetProperty
- func (op *SetProperty) Close() error
- func (op *SetProperty) Init(ctx context.Context) error
- func (op *SetProperty) Next(out *Row) (bool, error)
- func (op *SetProperty) PlanChildren() []Operator
- func (op *SetProperty) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *SetProperty
- func (op *SetProperty) WithParams(params map[string]expr.Value) *SetProperty
- func (op *SetProperty) WithRelCols(rc RelCols) *SetProperty
- func (op *SetProperty) WithValueEvalFn(fn ValueEvalFn) *SetProperty
- type ShortestPath
- func (op *ShortestPath) Close() error
- func (op *ShortestPath) Init(ctx context.Context) error
- func (op *ShortestPath) Next(out *Row) (bool, error)
- func (op *ShortestPath) PlanChildren() []Operator
- func (op *ShortestPath) WithHopBounds(minHops, maxHops int) *ShortestPath
- func (op *ShortestPath) WithOptional(optional bool) *ShortestPath
- func (op *ShortestPath) WithPathPredicate(pred func(Row) (bool, error)) *ShortestPath
- func (op *ShortestPath) WithTypeFilter(edgeType string) *ShortestPath
- func (op *ShortestPath) WithWorkBudget(maxPerRow, maxTotal int) *ShortestPath
- type SingleRow
- type Skip
- type SlabPool
- type Sort
- type SortKey
- type StaticRows
- type StorageRecordScan
- type StringHashIndex
- type StringRangeIndex
- type SubplanFactory
- type TargetEvalFn
- type Top
- type Union
- type UnionAll
- type Unwind
- type UnwindListFn
- type ValueEvalFn
- type VarLengthConfig
- type VarLengthExpand
Examples ¶
Constants ¶
const ( VLEDirForward = 0 VLEDirReverse = 1 )
VLEDirForward and VLEDirReverse are the direction-marker values stored at the third slot of each hop triple in the VarLengthExpand flat path list (stride VLEHopStride). A reverse marker tells the relationship hydrator to swap the storage endpoints when resolving the edge's per-instance type and properties, and tells the path renderer to emit `<-[…]-` (rmp #1685).
const DefaultChunkCapacity = 4096
DefaultChunkCapacity is the default per-column row capacity of a Chunk. It matches DefaultSlabCapacity so a Chunk aligns with the pipeline's row-batch boundary; the capacity is a pre-sizing hint (appends beyond it grow the backing), not a hard bound.
const DefaultMaxDistinct = 10_000_000
DefaultMaxDistinct is the default upper bound on distinct rows tracked by the Distinct operator.
const DefaultMaxEagerRows = 10_000_000
DefaultMaxEagerRows is the default upper bound on rows that Eager holds in memory. It matches the sibling pipeline-breaker caps (DefaultMaxSortRows, DefaultMaxDistinct).
const DefaultMaxGroups = 1_000_000
DefaultMaxGroups is the default upper bound on distinct groups that EagerAggregation will hold in memory.
const DefaultMaxSortRows = 10_000_000
DefaultMaxSortRows is the default upper bound on rows that Sort holds in memory.
const DefaultMorselSize = 1024
DefaultMorselSize is the number of NodeIDs processed per worker goroutine per scheduling quantum. Sized to fill roughly one or two cache lines of work before touching a channel.
const DefaultSlabCapacity = 4096
DefaultSlabCapacity is the default maximum number of rows a RowSlab holds before returning ErrSlabOverflow. It is sized to keep a typical pipeline batch within a few cache lines.
const MetricExpandIntersectEngaged = "cypher.expand_intersect.engaged"
MetricExpandIntersectEngaged counts how many times a fused cyclic expand was actually initialised for execution.
This is the white-box engagement counter, and it is not optional observability: SPIKE #2155 verified that the openCypher TCK contains NO directed cycle over three or more distinct node variables, so the 3897/3897 gate stays green whether this operator is correct, wrong, or never runs at all. A differential comparing the flag on against the flag off is likewise blind to an operator that silently declined to engage — both arms would simply run today's plan and agree. Only a counter distinguishes "identical because it is correct" from "identical because it never fired".
const VLEHopStride = 3
VLEHopStride is the per-hop element count of the flat alternating path list that VarLengthExpand emits. The list is
[srcNodeID, fwdPos0, dstNode0, dir0, fwdPos1, dstNode1, dir1, …]
so an N-hop path occupies 1 + VLEHopStride*N elements: the leading source node id, then one (forward edge position, destination node id, direction marker) triple per hop. The direction marker is VLEDirForward or VLEDirReverse. Readers in the cypher package (the path/relationship-list hydrators) and [appendExcludedFromValue] index the list with this stride; it is exported so those readers share the single source of truth (rmp #1685).
Before #1685 the stride was 2 ((edgePos, dst) pairs) and the edge position was synthetic for a reverse hop, which lost both the physical-edge identity and the traversal direction the hydrator needs for per-instance type and property reporting on multigraph parallel edges.
Variables ¶
var ErrAggMemoryExceeded = errors.New("exec: aggregation memory cap exceeded")
ErrAggMemoryExceeded is returned by EagerAggregation.Next when the number of distinct groups exceeds the configured maxGroups limit.
var ErrConstraintAlreadyExists = errors.New("exec: constraint already exists")
ErrConstraintAlreadyExists is the sentinel returned (wrapped) when CREATE CONSTRAINT (without IF NOT EXISTS) names a constraint whose (kind, label, property) identity is already registered. It is the analogue of Neo4j's EquivalentSchemaRuleAlreadyExists / ConstraintAlreadyExists — a constraint- level fault surfaced instead of leaking the synthetic backing-index name.
var ErrConstraintNameConflict = errors.New("exec: constraint name already in use")
ErrConstraintNameConflict is the sentinel returned (wrapped) when CREATE CONSTRAINT requests a name already held by a DIFFERENT constraint (a distinct kind/label/property). Neo4j requires constraint names to be unique across the database; this is the analogue of its ConstraintWithNameAlreadyExists.
var ErrConstraintNotFound = errors.New("exec: constraint not found")
ErrConstraintNotFound is the sentinel returned (wrapped) when a DROP CONSTRAINT names a constraint that does not exist and IF EXISTS was not given. It is the fail-stop analogue of Neo4j's ConstraintDropFailed / "No such constraint": the drop reports a typed error rather than fail-silently claiming success.
var ErrConstraintViolation = errors.New("exec: constraint violation")
ErrConstraintViolation is the sentinel returned (wrapped) by CheckSetProperty when a write would violate a constraint.
var ErrDeleteNodeHasRelationships = errors.New("exec: cannot delete node with existing relationships; use DETACH DELETE")
ErrDeleteNodeHasRelationships is returned when DELETE is attempted on a node that still has one or more incident relationships. Use DETACH DELETE to remove the node together with its relationships.
var ErrDistinctMemoryExceeded = errors.New("exec: distinct memory cap exceeded")
ErrDistinctMemoryExceeded is returned by Distinct.Next when the number of distinct rows seen exceeds the configured maxDistinct limit.
var ErrEagerMemoryExceeded = errors.New("exec: eager memory cap exceeded")
ErrEagerMemoryExceeded is returned by Eager.Init when the drained child produces more than maxRows rows.
var ErrHashJoinMemoryExceeded = errors.New("exec: hash join memory cap exceeded")
ErrHashJoinMemoryExceeded is returned by the HashJoin build phase when the estimated retained size of the build table exceeds the configured byte budget (#1841). The build table is otherwise unbounded, so without a budget a large build side could exhaust memory before any drain-level guard fires.
var ErrIndexTypeMismatch = errors.New("exec: index type mismatch")
ErrIndexTypeMismatch is returned by NodeByIndexSeek.Init when the seek value's Kind is incompatible with the index's key type.
var ErrNestedPropertyValue = errors.New("exec: InvalidPropertyType: a nested list or map is not a valid property value")
ErrNestedPropertyValue is returned when a property value is a nested collection — a list containing a list or a map. openCypher restricts a property value to a primitive or a flat list of primitives; a nested collection is InvalidPropertyType. Unlike a non-literal expression (which is deferred to a runtime PropsEvalFn), this sentinel is a hard, fail-stop error: the CREATE/MERGE literal builders re-raise it rather than silently skipping the property, so an invalid value is never stored (the storage layer cannot serialise a nested PropList) and never silently dropped (audit 2026-07-13 security F3).
var ErrProjectionRowTooLarge = errors.New("exec: projection row memory cap exceeded")
ErrProjectionRowTooLarge is returned by Project.Next when the estimated size of a single assembled output row exceeds the configured per-row byte budget. It bounds the transient peak of one row's construction (e.g. a RETURN that projects several large list columns) to the ceiling plus one column, independent of the column count, so a query cannot OOM the process by compounding many big columns into one row (#1852).
var ErrPropertyValueIsNull = errors.New("exec: property value is null (skip)")
ErrPropertyValueIsNull is the sentinel returned by [parsePropValue] when the value is the literal "null". By openCypher semantics, assigning null to a property removes it (or never sets it for a fresh node), so callers that catch this sentinel must skip the property entirely rather than surface a parse error.
var ErrSchemaMismatch = errors.New("exec: union schema mismatch: column counts differ")
ErrSchemaMismatch is returned when the left and right operands of a UNION produce rows with different column counts.
var ErrSeekSetOverBudget = errors.New("exec: index seek set exceeds its posting budget")
ErrSeekSetOverBudget is returned by NodeByIndexSeekSet.Init when the merged posting count exceeds the budget the operator was built with. It is a planning signal, not a failure: the caller answers the query by scanning instead.
var ErrSlabOverflow = errors.New("exec: row slab overflow")
ErrSlabOverflow is returned by RowSlab.Alloc when the slab has reached its capacity limit. Callers must flush or reset the slab before continuing.
var ErrSortMemoryExceeded = errors.New("exec: sort memory cap exceeded")
ErrSortMemoryExceeded is returned when Sort collects more than maxRows rows.
var ErrUnwindNilChild = errors.New("exec: NewUnwind requires non-nil child Operator")
ErrUnwindNilChild is returned by NewUnwind when child is nil.
var ErrUnwindNilListFn = errors.New("exec: NewUnwind requires non-nil listFn")
ErrUnwindNilListFn is returned by NewUnwind when listFn is nil.
var ErrVarLenCapExceeded = errors.New("exec: variable-length expand safety cap exceeded")
ErrVarLenCapExceeded is returned when a VarLengthExpand exceeds its configured maximum edge traversal count — either the per-input-row cap or the aggregate per-query cap (see [defaultMaxEdgesTraversed] and [defaultMaxTotalEdgesTraversed]).
Functions ¶
func BitSet ¶ added in v0.9.0
BitSet reports whether bit i of a packed validity bitmap is set (LSB-first, the Arrow convention). It is the canonical test for a raw bitmap returned by the vectorized column accessors when allValid is false.
func EmitsExactly ¶ added in v0.11.0
EmitsExactly reports whether op is statically known to emit exactly cols, in that order — both the column NAMES and the row ARITY.
The arity half is what makes it safe to act on. A caller holding only a variable-to-index schema can tell where a column SITS but not how WIDE the row is, so `MATCH (a)-[r]->(b:P) RETURN a, r` looks like an identity — both columns are already at their own index — while the row that actually arrives carries a third column that a passthrough projection must still narrow away. Project.Columns reports the real output arity, which closes that gap.
The walk descends through operators that re-emit their input row UNCHANGED in width and column order (see rowShapePreserving), so `RETURN n LIMIT 3` — Limit over Project — is recognised as readily as a bare `RETURN n`. Any operator that neither declares its columns nor preserves the row shape stops the descent and yields false, so a shape-changing operator can never be walked through by omission.
func EnforceUniqueOnLabelRemove ¶ added in v0.11.0
func EnforceUniqueOnLabelRemove( reg *ConstraintRegistry, mutator GraphMutator, nodeKey, label string, )
EnforceUniqueOnLabelRemove gives back what detaching label frees. It MUST run before the label is actually detached — it reads both the membership and the property values, and after the write neither is available. See EnforceUniqueOnLabelSet for the placement rationale.
func EnforceUniqueOnLabelSet ¶ added in v0.11.0
func EnforceUniqueOnLabelSet( reg *ConstraintRegistry, mutator GraphMutator, mgr *index.Manager, nodeKey, label string, ) error
EnforceUniqueOnLabelSet and EnforceUniqueOnLabelRemove are the LABEL half of the choke point (rmp #2358): the mutator adapter calls them from inside its own SetNodeLabel / RemoveNodeLabel, so an operator that writes a label cannot skip enforcement by forgetting to call anything.
Why enforcement moved here, and what it is worth ¶
GoGraph used to enforce UNIQUE at each write SITE. Nothing in that design forced a NEW site to enforce anything: an operator that wrote a label and forgot to reserve compiled, passed every existing test, and silently admitted duplicates. That happened twice in one sprint — the SetLabels operator (rmp #2352), then both MERGE action paths — which is why the placement, not the semantics, was the defect.
Both reference engines enforce at the single write surface every operator must traverse. Memgraph's VertexAccessor::AddLabel does both halves itself (src/storage/v2/vertex_accessor.cpp:232, commit 343f7fe); Neo4j's Operations.nodeAddLabel validates through checkConstraintsAndAddLabelToNode BEFORE txState().nodeDoAddLabel, and nodeRemoveLabel is symmetric (community/kernel/.../newapi/Operations.java:791-850, commit eccd584). GoGraph already used this shape for its own NOT NULL enforcement — the touch lives in the adapters — so UNIQUE was the outlier.
The zero-constraint path stays free ¶
ConstraintRegistry.HasAnyUnique is a lock-free atomic load and it is the gate here, so a schema with no UNIQUE constraint pays one nil check and one atomic load per label write and nothing else: no registry lock, no graph read, no allocation. That matters — [ConstraintRegistry.uniqueActive] records this registry's lock at 57 % of ALL lock delay at sixteen writers on a schema with no constraints at all.
The caller passes ITSELF as mutator, which is what makes the transaction-visible reads ([labelsInTx], [txVisibleNodeReader]) resolve through the writing transaction rather than the raw graph.
func EnforceUniqueOnPropertyDelete ¶ added in v0.11.0
func EnforceUniqueOnPropertyDelete( reg *ConstraintRegistry, mutator GraphMutator, nodeKey, key string, )
EnforceUniqueOnPropertyDelete gives back what removing a property frees, so a later legitimate write of the same value is not refused by a phantom.
It MUST run before the property is removed: it reads the value it is releasing. SET n.k = null is a removal and reaches here through the same door.
func EnforceUniqueOnPropertySet ¶ added in v0.11.0
func EnforceUniqueOnPropertySet( reg *ConstraintRegistry, mutator GraphMutator, mgr *index.Manager, nodeKey, key string, value lpg.PropertyValue, ) error
EnforceUniqueOnPropertySet is the PROPERTY half of the choke point (rmp #2358): the mutator adapter calls it from inside its own SetNodeProperty, so an operator that writes a property cannot skip enforcement.
The release must precede the check, and it is not an optimisation ¶
The node's OWN old value is released first, even when it equals the new one. Without that, an idempotent self-set — SET n.k = n.k, and every statement that rewrites a value unchanged — would be rejected as a duplicate of ITSELF. Releasing first cannot mask a real cross-node duplicate, because a UNIQUE constraint guarantees at most one holder of a value: whatever is released was this node's.
Which labels ¶
[labelsInTx], never the raw label set: a UNIQUE constraint attaches to a label, so deciding what to reserve starts by asking which labels the node carries, and asking the raw graph would include another in-flight transaction's uncommitted label writes. That is the defect rmp #2355 fixed at each site individually, and having ONE site is what stops the next one from drifting.
The old value is likewise read through the transaction's view rather than the raw store — for the same reason, and because a value this transaction has already written is the one it must give back.
Callers gate on nothing: the zero-constraint check is here, and it is a lock-free atomic load.
func ExpandIntoSeekCount ¶ added in v0.11.0
func ExpandIntoSeekCount() uint64
ExpandIntoSeekCount reports how many times an expand-into hop has narrowed its FORWARD cursor to a bound destination's run since process start. It is a diagnostic seam for tests that must prove the seek actually FIRED — an EXPLAIN line proves the plan, this proves the access path — and for operational observability. Process-global and monotonic; callers snapshot it before and after a query rather than resetting it.
func ExpandIntoSeekReverseCount ¶ added in v0.11.0
func ExpandIntoSeekReverseCount() uint64
ExpandIntoSeekReverseCount reports the same for the REVERSE cursor, which a DirIn or DirBoth closing hop narrows.
It is counted separately because the two are independently defeatable and a result comparison cannot tell them apart: dropping the reverse narrowing makes the operator fall back to walking the whole in-edge range, which is SLOWER but returns exactly the same rows in the same order. Without its own counter that regression is invisible to every differential test — verified by injecting it.
func MergeActionEvalKey ¶ added in v0.8.0
MergeActionEvalKey composes the map key under which a MERGE ON CREATE / ON MATCH property-set action's per-row RHS evaluator is registered and looked up. targetVar is the entity variable the action writes (a node, a bound endpoint, or a relationship variable) and key is the property key. The NUL separator cannot appear in a Cypher identifier, so the composed key is unambiguous across distinct (variable, property) pairs. The physical builder ([cypher] package) and the operators here must agree on this encoding, so it is defined once and exported.
func NewUniqueBackingIndex ¶ added in v0.2.0
func NewUniqueBackingIndex() index.Subscriber
NewUniqueBackingIndex returns a fresh unbound hash-index subscriber. It is kept for callers (tests, recovery) that do not have a live graph and therefore cannot build a bound index. The engine's write path always supplies a bound subscriber via CreateConstraintOp.WithBackingIndex so the index self-maintains from the change fan-out.
func PropMapContainsNullLiteral ¶
PropMapContainsNullLiteral reports whether the property-map source string contains any value that is the literal `null`. It is used by MERGE to surface the openCypher `MergeReadOwnWrites` error when a merge predicate contains a null property value — such a merge can never match its own write because null comparisons are always tri-valued false, so the engine rejects the pattern outright. Used at MERGE plan-build time; CREATE silently drops null-valued properties so it does NOT use this check.
The argument may be either a single map literal "{k: v, …}" or a larger surface form (e.g. a full pattern string "(a)-[r:T {k: null}]->(b)") in which case every embedded balanced "{...}" segment is scanned. The check splits each map at top-level commas, isolates each value substring, and reports true if any value (case-insensitively) equals the bare token `null`. Variable refs and expressions are ignored — they parse to non-literal forms.
func RenderPlan ¶ added in v0.11.0
RenderPlan renders the physical plan rooted at op as an indented tree, in the same shape the logical-plan renderer uses so the two read alike:
ProduceResults
└─ EagerAggregation
└─ HashJoin [build=a, probe=b]
├─ NodeByLabelScan [a:P]
└─ NodeByLabelScan [b:P]
A profiled plan appends each operator's emitted rows and self time.
func RenderPlanNode ¶ added in v0.11.0
RenderPlanNode renders an already-captured tree, so a caller that kept a PlanNode (for example from a profiling run whose operators are closed) can still print it. It does not modify n.
When any node in the tree carries measurements, the ones that do not are labelled "(not measured)" rather than left bare. That distinction is load bearing: a bare node in a profiled plan would read as an operator that cost nothing, when in fact it was never instrumented.
Such nodes USED to exist: instrumentation is applied at one point, the value the recursive builder returns, and a composite lowering emits several operators for a single logical node, of which only the outermost passed through it. rmp #2237 closed that by instrumenting each composite site, and TestProfile_EveryOperatorIsMeasured holds it closed. The label is kept because naming an unmeasured node honestly is still preferable to hiding it or inventing a zero, and a future composite lowering could reopen the gap.
func UniqueIndexName ¶ added in v0.2.0
UniqueIndexName returns the deterministic synthetic name of the hash index that backs a UNIQUE constraint on (label, prop). It is exported so the engine can re-create the same backing index when re-registering a constraint recovered from disk, keeping the name in lockstep with the one the CreateConstraint operator uses.
func WithCyphermorphism ¶
func WithCyphermorphism(relCols []int) expandOption
WithCyphermorphism returns an option that enables cyphermorphism enforcement. relCols lists the column indices in each input row that hold existing edge IDs (as expr.IntegerValue). When Expand is about to emit a row with a new edgeID, it rejects the row if edgeID equals any value already present in those columns.
Types ¶
type AdjacencySource ¶ added in v0.11.0
type AdjacencySource func() (fwd, rev CSRAdjacency, edgeTypeFilter map[uint64]string)
AdjacencySource yields the adjacency a traversal expands over, RESOLVED AT THE MOMENT IT IS CALLED rather than when the plan was built (rmp #2317).
Why a source and not a pair ¶
A relationship traversal used to receive two prebuilt CSRs, materialised while the operator tree was being assembled — before any row executed. That froze the topology to an instant chosen before the statement began, and it is why a later clause of a statement could not observe an earlier edge CREATE or edge DELETE while it observed every node write: the node side reads live stores, the edge side read a frozen array.
Both reference engines resolve relationships at execution time against the transaction's own view — Memgraph's Expand::ExpandCursor::InitEdges goes through vertex.OutEdges with a storage::View, and Neo4j's query context resolves relationships per row rather than from a plan-time structure.
The type filter is part of the source, not a separate config field, because it is KEYED to the adjacency it was built against. Resolving one without the other would apply a filter built for one topology to a different one.
It is called from Expand.Init, which runs once per outer row under Apply, so the traversal follows the writes its own statement has made.
func StaticAdjacency ¶ added in v0.11.0
func StaticAdjacency(fwd, rev CSRAdjacency, edgeTypeFilter map[uint64]string) AdjacencySource
StaticAdjacency is an AdjacencySource over a fixed pair, for callers that genuinely hold one — an offline traversal, or a test that builds its own CSR. A production query plan must NOT use it: the pair it closes over is exactly the plan-build materialisation this type exists to remove.
type AggInputFactory ¶ added in v0.10.0
AggInputFactory builds an independent sub-plan that emits the PRE-AGGREGATION rows for exactly the node IDs in ids: one row per node in scan order, laid out as [groupKey0..groupKey{nKeys-1}, aggArg0..aggArg{nAggs-1}] — the same layout EagerAggregation's pre-projection installs, evaluated by the same closures, so each worker's per-node key/argument values are byte-identical to serial. Each call must return a fresh operator sharing NO mutable state with any other call's (the planner rebuilds the pre-projection over a per-worker walker and a per-worker buildOpts copy). The ids slice is owned by the caller and is read-only for the operator's lifetime. The returned operator is driven Init → Next* → Close by exactly one worker goroutine.
type AggReducerKind ¶ added in v0.10.0
type AggReducerKind uint8
AggReducerKind identifies the deterministic, byte-identical-to-serial combine a ParallelAggregateScan reducer applies to one aggregate column. Only these four kinds have an admitted parallel combine; the planner emits no other kind.
const ( // ReduceCountStar counts every row (count(*)). The combine is int64 addition. ReduceCountStar AggReducerKind = iota // ReduceCount counts non-NULL argument values (count(v)). The combine is int64 // addition; the NULL-skip is a per-value predicate independent of partition. ReduceCount // ReduceMin keeps the minimum argument value under [expr.Compare], ties broken // by lowest global scan index (position-carrying). NULLs are skipped. ReduceMin // ReduceMax keeps the maximum argument value under [expr.Compare], ties broken // by lowest global scan index (position-carrying). NULLs are skipped. ReduceMax )
type AllNodesCountScan ¶ added in v0.10.0
type AllNodesCountScan struct {
// contains filtered or unexported fields
}
AllNodesCountScan is a Volcano leaf operator that computes a group-by-less count over a bare full-node scan by reading the graph's live-node count directly. It emits exactly one row with a single expr.IntegerValue column carrying that count.
AllNodesCountScan is NOT safe for concurrent use.
func NewAllNodesCountScan ¶ added in v0.10.0
func NewAllNodesCountScan(g nodeWalker) *AllNodesCountScan
NewAllNodesCountScan creates an AllNodesCountScan over g.
func (*AllNodesCountScan) Close ¶ added in v0.10.0
func (op *AllNodesCountScan) Close() error
Close releases resources held by the operator. AllNodesCountScan holds none, so Close is a no-op and is safe to call whether or not Next was ever called.
func (*AllNodesCountScan) Init ¶ added in v0.10.0
func (op *AllNodesCountScan) Init(ctx context.Context) error
Init reads the live-node count once. It prefers the O(1) direct counter when g supports it and otherwise falls back to a single WalkNodeIDs count pass — both yield the same tombstone-excluded live count.
type AllNodesScan ¶
type AllNodesScan struct {
// contains filtered or unexported fields
}
AllNodesScan is a Volcano leaf operator that produces one Row per node in the graph. Each Row has a single column: an expr.IntegerValue holding the node's graph.NodeID cast to int64.
AllNodesScan is NOT safe for concurrent use.
func NewAllNodesScan ¶
func NewAllNodesScan(g nodeWalker) *AllNodesScan
NewAllNodesScan creates an AllNodesScan over g.
func (*AllNodesScan) Close ¶
func (op *AllNodesScan) Close() error
Close releases resources. The collected nodeIDs slice is retained (but its length zeroed) to allow reuse if Init is called again.
func (*AllNodesScan) FillChunk ¶ added in v0.9.0
func (op *AllNodesScan) FillChunk(dst *Chunk, maxRows int) (int, error)
FillChunk appends up to maxRows more NodeIDs, as unboxed int64, into column 0 of dst and returns the number appended (0 at end-of-stream). It is the column-major counterpart of AllNodesScan.Next: the SAME nodeIDs in the SAME order (advancing the shared op.pos cursor), but written to a typed column with no per-row heap box. Only one of Next/FillChunk drives a given query — the result sink picks the columnar drain or the row drain once — so sharing op.pos between them is sound. It honours context cancellation. It implements ChunkProducer.
func (*AllNodesScan) Init ¶
func (op *AllNodesScan) Init(ctx context.Context) error
Init collects all NodeIDs from the graph into an internal slice. The collection itself honours ctx cancellation every 4096 nodes.
func (*AllNodesScan) NewOutputChunk ¶ added in v0.9.0
func (op *AllNodesScan) NewOutputChunk(capacity int) *Chunk
NewOutputChunk returns a Chunk with a single static integer column that AllNodesScan fills with unboxed NodeIDs. It implements ChunkProducer (#1704 P3): a columnar-aware parent drains the scan column-major, avoiding the per-row expr.Value box AllNodesScan.Next pays.
type AllShortestPaths ¶
type AllShortestPaths struct {
// contains filtered or unexported fields
}
AllShortestPaths is a Volcano pipeline operator that, for each input row, finds all paths of minimum length from srcCol to dstCol and emits one output row per path, each carrying the flat alternating path list (stride VLEHopStride).
AllShortestPaths is NOT safe for concurrent use.
func NewAllShortestPaths ¶
func NewAllShortestPaths(input Operator, src AdjacencySource, dir Direction, srcCol, dstCol int) *AllShortestPaths
NewAllShortestPaths creates an AllShortestPaths operator. Like NewShortestPath it starts with no type filter and minHops == 1; configure via AllShortestPaths.WithTypeFilter and AllShortestPaths.WithHopBounds.
func (*AllShortestPaths) Close ¶
func (op *AllShortestPaths) Close() error
Close closes the input operator.
func (*AllShortestPaths) Init ¶
func (op *AllShortestPaths) Init(ctx context.Context) error
Init initialises the operator.
func (*AllShortestPaths) Next ¶
func (op *AllShortestPaths) Next(out *Row) (bool, error)
Next emits one row per shortest path per input row. With no path: one Null-path row (OPTIONAL MATCH) or no row (MATCH).
func (*AllShortestPaths) PlanChildren ¶ added in v0.11.0
func (op *AllShortestPaths) PlanChildren() []Operator
PlanChildren reports the operator whose rows it searches paths from.
func (*AllShortestPaths) WithHopBounds ¶ added in v0.6.0
func (op *AllShortestPaths) WithHopBounds(minHops, maxHops int) *AllShortestPaths
WithHopBounds sets the accepted path-length window. It returns op for chaining.
func (*AllShortestPaths) WithOptional ¶ added in v0.6.0
func (op *AllShortestPaths) WithOptional(optional bool) *AllShortestPaths
WithOptional selects the OPTIONAL MATCH no-path behaviour (emit a single Null-path row) when optional is true; the default (false) drops the row. It returns op for chaining.
func (*AllShortestPaths) WithPathPredicate ¶ added in v0.6.0
func (op *AllShortestPaths) WithPathPredicate(pred func(Row) (bool, error)) *AllShortestPaths
WithPathPredicate fuses a whole-path predicate onto the operator (#1786). The operator then returns ALL shortest paths that SATISFY pred (an exhaustive search at the minimum satisfying length), instead of all unconstrained shortest paths. pred is called with each candidate's full output row. It returns op for chaining.
func (*AllShortestPaths) WithTypeFilter ¶ added in v0.6.0
func (op *AllShortestPaths) WithTypeFilter(edgeType string) *AllShortestPaths
WithTypeFilter restricts traversal to edges whose forward position is present in filter. It returns op for chaining.
func (*AllShortestPaths) WithWorkBudget ¶ added in v0.7.0
func (op *AllShortestPaths) WithWorkBudget(maxPerRow, maxTotal int) *AllShortestPaths
WithWorkBudget overrides the exhaustive path-predicate search's per-input-row and aggregate per-query edge-traversal caps (see ShortestPath.WithWorkBudget). A non-positive value leaves the corresponding default in place. It returns op for chaining and is primarily a testing seam; production uses the defaults.
type AntiSemiApply ¶
type AntiSemiApply struct {
// contains filtered or unexported fields
}
AntiSemiApply emits each outer row for which the inner sub-plan produces zero rows.
AntiSemiApply is NOT safe for concurrent use.
func NewAntiSemiApply ¶
func NewAntiSemiApply(outer, inner Operator, arg *Argument) *AntiSemiApply
NewAntiSemiApply creates an AntiSemiApply operator.
- outer is the driving (left) plan.
- inner is the correlated (right) sub-plan whose leaf is arg.
- arg is the Argument node seeded with each outer row before inner Init.
func (*AntiSemiApply) Init ¶
func (op *AntiSemiApply) Init(ctx context.Context) error
Init initialises the outer plan.
func (*AntiSemiApply) Next ¶
func (op *AntiSemiApply) Next(out *Row) (bool, error)
Next advances to the next outer row for which the inner plan has zero results.
func (*AntiSemiApply) PlanChildren ¶ added in v0.11.0
func (op *AntiSemiApply) PlanChildren() []Operator
PlanChildren reports the outer input that drives this operator, then the inner sub-plan it re-drives for each outer row.
type Apply ¶
type Apply struct {
// contains filtered or unexported fields
}
Apply is a Volcano pipeline operator that performs a dependent (correlated) join: for each outer row, it re-runs the inner plan seeded with that row and emits one output row per inner result.
Apply is NOT safe for concurrent use.
func NewApply ¶
NewApply creates an Apply operator.
- outer is the left (driving) plan.
- inner is the right (sub) plan; its leaf must be the provided arg.
- arg is the Argument node at the root of inner; Apply seeds it before each inner Init call.
Apply takes ownership of both plans. The caller must not use outer or inner directly after calling NewApply.
func (*Apply) Init ¶
Init initialises both the outer plan and stores ctx for subsequent Next calls. The inner plan is initialised lazily on the first outer row.
func (*Apply) Next ¶
Next advances the Apply operator:
- If there is no current inner row available, it pulls the next outer row, seeds and re-inits the inner plan, then pulls from the inner plan.
- Returns each combined (outer || inner) row.
- Returns (false, nil) when the outer plan is exhausted.
func (*Apply) PlanChildren ¶ added in v0.11.0
PlanChildren reports the outer input that drives this operator, then the inner sub-plan it re-drives for each outer row.
type Argument ¶
type Argument struct {
// contains filtered or unexported fields
}
Argument is a Volcano leaf operator that emits the single outer Row injected by an Apply driver. It emits exactly one row per Init/Next cycle.
Argument is NOT safe for concurrent use.
func NewArgument ¶
func NewArgument() *Argument
NewArgument creates an Argument operator with no initial row. The Apply driver must call [SetOuterRow] before the first Init/Next cycle.
func (*Argument) Close ¶
Close is a no-op; Argument holds no resources beyond the outer row reference.
func (*Argument) Init ¶
Init resets the emitted flag so that the next Next call returns the outer row. It does not require a child operator.
func (*Argument) Next ¶
Next emits the outer row exactly once per Init call. Subsequent calls return (false, nil) until Init is called again.
func (*Argument) SetOuterRow ¶
SetOuterRow injects the current outer row. It must be called by the Apply driver before each Init/Next cycle for the inner plan.
type CSRAdjacency ¶ added in v0.11.0
type CSRAdjacency interface {
// VerticesSlice returns the CSR offsets array (length MaxNodeID+1).
VerticesSlice() []uint64
// EdgesSlice returns the flat neighbour array.
EdgesSlice() []graph.NodeID
// HandlesSlice returns the per-slot stable edge handles parallel to
// EdgesSlice, or nil when the snapshot carries no handles (a
// non-multigraph never built via AddEdgeH). The forward and reverse
// CSRs of the same graph carry the SAME handle for a given logical
// edge, which is what lets the reverse traversal recover per-instance
// edge identity across parallel edges (rmp #1634).
HandlesSlice() []uint64
}
CSRAdjacency is the minimal interface required from a CSR snapshot. csr.CSR[W] satisfies this interface for any W.
It is exported so a query planner in another package can supply an AdjacencySource that resolves one at execution time (rmp #2317).
type Chunk ¶ added in v0.9.0
type Chunk struct {
// contains filtered or unexported fields
}
Chunk is a column-major (struct-of-arrays) execution batch, the foundation for late materialization in the Cypher executor (rmp #1704, DuckDB DataChunk style). The row-at-a-time model (Row = []expr.Value) boxes every scalar into the expr.Value interface once per cell; a Chunk instead keeps scalar columns in contiguous, unboxed, typed backing slices ([]int64/[]float64/[]string/ []bool) so that later phases can scan them without interface dispatch and box only at the sink (Chunk.BoxCell/Chunk.BoxRow).
This type is purely additive: as of its introduction NO operator is wired to it. It lands the layout, its API, and its allocation discipline behind the existing Operator interface so later phases can migrate operators onto it without changing observable behaviour.
Design (validated with the columnar-db-expert against DuckDB / Apache Arrow) ¶
- Struct-of-arrays: a single logical row count (length) and capacity live at the Chunk level, never per column, so columns cannot silently drift.
- Discriminated-fields column (a tagged union): each [column] holds every possible typed backing as a separate field and a storage tag selecting the live one. Only the live backing is allocated; the rest stay nil (GC leaves). This avoids both interface{} at the Chunk boundary (which a generic Column[T] behind a non-generic holder would reintroduce) and unsafe reinterpretation. The scalar type set is closed by the Cypher language, so a tagged union is the idiomatic fit.
- Validity: a packed bitmap ([]uint64, 1 bit per row, bit set = valid / non-null, LSB-first) per column, matching the Arrow columnar spec. A sentinel value is impossible (0 is a valid int, NaN a valid float, "" a valid string); a []bool mask wastes 8x the memory. An allValid fast path (an explicit flag, not an overloaded nil) skips the bitmap entirely while a column has seen no NULL; the bitmap is materialized lazily on the first NULL and, under pooling, its backing is retained across Chunk.Reset.
- String columns use []string for this phase. An offsets+bytes arena (Arrow-style) is the eventual memory win but is deferred: the strings the engine produces already exist as Go strings, so []string costs only a 16-byte header copy and no new byte allocation, which is enough to remove the per-cell interface box that #1704 targets. The cell accessor Chunk.String returns a string (a view), so a later arena swap behind it is non-breaking; Chunk.StringColumn returning []string is Phase-1 only.
- Box-at-sink: the validity bitmap is authoritative for every column kind (including boxed); a NULL cell boxes to expr.Null regardless of the backing slot's value.
Deferred, deliberately (foundational decisions recorded for later phases) ¶
- Selection vectors: accessors index rows PHYSICALLY (logical index == physical index). Late-materialization filtering via a selection vector (DuckDB SelectionVector) is a named later phase; callers must not assume the physical-index contract is permanent.
- Dynamic type promotion: a column's storage is fixed at construction from its declared expr.Kind. Scalar kinds map to typed backings; every other kind (List/Map/Node/Relationship/Path/temporals) maps to a boxed []expr.Value backing, which also serves genuinely heterogeneous columns. A typed column rejects a value of a different kind (programmer error); promote-a-typed-column-to-boxed-on-conflict is deferred. Declare heterogeneous columns with a non-scalar kind so they are boxed from the start.
Concurrency ¶
A Chunk is NOT safe for concurrent use. Each pipeline stage owns its own instance, typically obtained from a ChunkPool.
func NewChunk ¶ added in v0.9.0
NewChunk creates an empty Chunk with one column per kind in kinds, each pre-sized to capacity rows. A capacity < 1 defaults to DefaultChunkCapacity. Scalar kinds (Integer/Float/String/Bool) get a typed backing; every other kind gets a boxed []expr.Value backing. The returned Chunk has length 0.
func NewDynamicChunk ¶ added in v0.9.0
NewDynamicChunk creates an empty Chunk with ncols dynamic columns and a row capacity of capacity. A capacity < 1 defaults to DefaultChunkCapacity.
Unlike NewChunk the constructor allocates no backing, because it cannot: a dynamic column's type is not known until its first value arrives. The backing is instead allocated — once, pre-sized to capacity — by the Put that commits the column to a type (see [Chunk.commitDynamic]), so a dynamic column pays the same single sized allocation a statically declared one does.
A dynamic column has no declared kind and no backing at construction: the first Chunk.PutInt64/Chunk.PutFloat64/Chunk.PutString/Chunk.PutBool COMMITS it to the matching typed backing, and a later value of a conflicting scalar kind — or any non-scalar value via Chunk.PutValue — PROMOTES it to a boxed []expr.Value backing (re-boxing the values already stored). This is the dynamic type promotion the Chunk type documentation defers for statically constructed columns; it is what the Cypher late-materialisation projection needs, because a property column's kind is not known until the values are read (openCypher permits a property to carry different types across nodes). A column whose first appended value is NULL commits to boxed (a null-first column stays boxed — a correct, minor missed optimisation). Box-at-sink (Chunk.BoxCell) is unaffected: a typed cell boxes to the identical expr.Value a boxed cell would, and a NULL boxes to expr.Null.
func (*Chunk) AppendBool ¶ added in v0.9.0
AppendBool appends v to bool column j, marking it non-null. It panics if column j is not a bool column.
func (*Chunk) AppendFloat64 ¶ added in v0.9.0
AppendFloat64 appends v to float column j, marking it non-null. It panics if column j is not a float column.
func (*Chunk) AppendInt64 ¶ added in v0.9.0
AppendInt64 appends v to integer column j, marking it non-null. It panics if column j is not an integer column.
func (*Chunk) AppendNull ¶ added in v0.9.0
AppendNull appends a NULL to column j. A placeholder zero value is written to the live backing to keep row indices aligned; the validity bitmap records the NULL, and it is the bitmap — never the placeholder — that box-at-sink honours.
It escalates through [growTo] for the same reason the push helpers do: a column fed mostly NULLs — an OPTIONAL MATCH that misses, a projection over a sparse property — reaches the batch through this path and nothing else, and without the escalation it would walk the doubling series up from [dynamicCommitFloor] that growTo exists to avoid.
func (*Chunk) AppendRowFrom ¶ added in v0.9.0
AppendRowFrom appends the whole logical row srcRow of src to c, one cell per column, WITHOUT boxing a scalar: each source cell is copied into c's matching column through the typed append primitives, and a NULL source cell appends a NULL. It is the row-compaction primitive ColumnarFilter uses to copy a passing row from its source batch into its output batch. c and src MUST have the same column count and matching per-column storage (the filter's output chunk is built from the source producer's schema), otherwise the typed append panics on a kind mismatch. It panics on an out-of-range srcRow.
func (*Chunk) AppendString ¶ added in v0.9.0
AppendString appends v to string column j, marking it non-null. It panics if column j is not a string column.
func (*Chunk) AppendValue ¶ added in v0.9.0
AppendValue appends a boxed expr.Value to column j at the sink boundary, routing it to the column's typed backing. A nil interface or expr.Null appends a NULL. For a typed (scalar) column the value's concrete type must match the column's storage, otherwise AppendValue panics (dynamic promotion is deferred; see the type doc). A boxed column accepts any value.
func (*Chunk) Bool ¶ added in v0.9.0
Bool returns the value and validity of row in bool column j. See Chunk.Int64 for the NULL and panic contract.
func (*Chunk) BoolColumn ¶ added in v0.9.0
BoolColumn returns the backing of bool column j. It panics on a kind mismatch.
func (*Chunk) BoxCell ¶ added in v0.9.0
BoxCell boxes cell (j, row) back to an expr.Value. A NULL cell (validity 0) boxes to expr.Null regardless of the backing slot's value. This, and Chunk.BoxRow, are the only places boxing happens once operators are wired. It panics on an out-of-range row.
func (*Chunk) BoxRow ¶ added in v0.9.0
BoxRow boxes the whole logical row at index row into dst, one expr.Value per column, and returns dst (reused when it has capacity, else freshly allocated). It is the row-materialization primitive later phases use to emit a Row at the sink. It panics if the Chunk is ragged (columns of unequal length) or row is out of range.
func (*Chunk) Cap ¶ added in v0.9.0
Cap returns the per-column row capacity hint the Chunk was constructed with.
func (*Chunk) CopyCellTo ¶ added in v0.9.0
CopyCellTo copies cell (srcCol, srcRow) of c into column dstCol of dst WITHOUT boxing a plain scalar: a typed cell is appended through dst's matching dynamic Chunk.PutInt64/Chunk.PutFloat64/Chunk.PutString/Chunk.PutBool, a boxed cell through Chunk.PutValue (which re-routes a plain scalar back to a typed backing and keeps every other kind boxed), and a NULL cell through Chunk.PutNull. It reads nothing but the already-materialised value the source chunk holds — no graph access — so it is the scalar-column passthrough primitive (rmp #2045): a projection over an already-columnar child copies a materialised value from the child's chunk instead of re-boxing it row-at-a-time or re-reading the graph by NodeID (which the box-at-sink isolation contract forbids). dst columns must be dynamic or match the source kind — the Chunk.PutValue/Put* family promotes a dynamic column on a kind conflict rather than panicking, so a heterogeneous passthrough column stays byte-identical to the row-at-a-time path. It panics on an out-of-range srcRow.
func (*Chunk) Float64 ¶ added in v0.9.0
Float64 returns the value and validity of row in float column j. See Chunk.Int64 for the NULL and panic contract.
func (*Chunk) Float64Column ¶ added in v0.9.0
Float64Column returns the backing of float column j. It panics on a kind mismatch.
func (*Chunk) Int64 ¶ added in v0.9.0
Int64 returns the value and validity of row in integer column j. When the cell is NULL the returned value is the (meaningless) placeholder and valid is false; callers must check valid. It panics on a kind mismatch or an out-of-range row.
func (*Chunk) Int64Column ¶ added in v0.9.0
Int64Column returns the backing of integer column j. It panics on a kind mismatch.
func (*Chunk) IsBoolColumn ¶ added in v0.9.0
IsBoolColumn reports whether column j's live backing is an unboxed []bool. See Chunk.IsFloat64Column.
func (*Chunk) IsFloat64Column ¶ added in v0.9.0
IsFloat64Column reports whether column j's live backing is an unboxed []float64. Together with Chunk.IsInt64Column/Chunk.IsStringColumn/ Chunk.IsBoolColumn it lets a consumer (e.g. the columnar EagerAggregation grouping-key path, #2049) read a scalar cell unboxed via the typed accessor for that kind and fall back to Chunk.BoxCell for a boxed or promoted column.
func (*Chunk) IsInt64Column ¶ added in v0.9.0
IsInt64Column reports whether column j's live backing is an unboxed int64 slice — the storage the scan emits for a raw NodeID column. The columnar projection's chunk-input fast path uses it to confirm a source column really holds raw NodeIDs before reading them via Chunk.Int64, falling back to the boxed row path otherwise so a non-int64 column never triggers a kind-mismatch panic on the read path.
func (*Chunk) IsNull ¶ added in v0.9.0
IsNull reports whether row of column j is NULL. It panics on an out-of-range row.
func (*Chunk) IsStringColumn ¶ added in v0.9.0
IsStringColumn reports whether column j's live backing is an unboxed []string. See Chunk.IsFloat64Column.
func (*Chunk) IsValid ¶ added in v0.9.0
IsValid reports whether row of column j is non-null. It panics on an out-of-range row.
func (*Chunk) Len ¶ added in v0.9.0
Len returns the logical row count. For a well-formed (rectangular) Chunk every column shares this length; it is the length of the first column, or 0 when the Chunk has no columns.
func (*Chunk) PutBool ¶ added in v0.9.0
PutBool appends v as a bool to column j, committing or promoting its backing as described in NewDynamicChunk.
func (*Chunk) PutFloat64 ¶ added in v0.9.0
PutFloat64 appends v as a float to column j, committing or promoting its backing as described in NewDynamicChunk.
func (*Chunk) PutInt64 ¶ added in v0.9.0
PutInt64 appends v as an integer to column j, committing or promoting its backing as described in NewDynamicChunk.
func (*Chunk) PutNull ¶ added in v0.9.0
PutNull appends a NULL to column j. On a dynamic column the first value being NULL commits the column to a boxed backing (a null-first column stays boxed); on an already-committed column it records a NULL in the live backing exactly like Chunk.AppendNull. The validity bitmap — never the placeholder — is what box-at-sink honours.
func (*Chunk) PutString ¶ added in v0.9.0
PutString appends v as a string to column j, committing or promoting its backing as described in NewDynamicChunk.
func (*Chunk) PutValue ¶ added in v0.9.0
PutValue appends a boxed expr.Value to column j, routing a plain scalar (Integer/Float/String/Bool) to the typed fast paths — so a fallback that produces an already-boxed scalar keeps the column typed — and boxing every other kind (temporal / point / list / map / node / …) into the boxed backing, promoting the column if necessary. A nil interface or expr.Null appends a NULL. This is the sink-boundary entry point the columnar projection uses for values it must keep boxed for byte-identity with the row-at-a-time path.
func (*Chunk) Reset ¶ added in v0.9.0
func (c *Chunk) Reset()
Reset clears every column for reuse while retaining the backing allocations. It resets the length to 0 and, per column: leaves fixed-width backings (int64/float64/bool) untouched (they hold no pointers, so stale values are never traced by the GC and are invisible past length 0); nils the used slots of string and boxed backings (their headers hold pointers and would otherwise pin memory); zeroes the validity bitmap words while keeping the bitmap backing; and restores the allValid fast path.
func (*Chunk) RowByteEstimate ¶ added in v0.9.0
func (c *Chunk) RowByteEstimate(row int, overhead int64, estimateBoxed func(expr.Value) int64) int64
RowByteEstimate returns a coarse, allocation-free byte estimate for the whole logical row at index row, for a columnar sink's byte-budget accounting: per column it charges overhead for a NULL cell or a fixed-width scalar (int64/float64/bool), overhead plus the byte length for a string cell, and estimateBoxed applied to the stored expr.Value for a boxed cell. It boxes nothing. Summed this way it equals what a per-value estimator (overhead-based, string-length-aware) summed over the row's boxed values would yield, so a columnar drain's byte budget trips at the same point the row-oriented drain does. It panics on an out-of-range row.
func (*Chunk) SetBool ¶ added in v0.9.0
SetBool overwrites row of bool column j with v, marking it non-null. See Chunk.SetInt64 for the row and panic contract.
func (*Chunk) SetFloat64 ¶ added in v0.9.0
SetFloat64 overwrites row of float column j with v, marking it non-null. See Chunk.SetInt64 for the row and panic contract.
func (*Chunk) SetInt64 ¶ added in v0.9.0
SetInt64 overwrites row of integer column j with v, marking it non-null. row must be an already-appended row (0 <= row < column length). It panics on a kind mismatch or an out-of-range row.
func (*Chunk) SetNull ¶ added in v0.9.0
SetNull marks row of column j as NULL, releasing any reference the slot held (string header or boxed value) so the batch does not pin it. See Chunk.SetInt64 for the row and panic contract.
func (*Chunk) SetString ¶ added in v0.9.0
SetString overwrites row of string column j with v, marking it non-null. See Chunk.SetInt64 for the row and panic contract.
func (*Chunk) String ¶ added in v0.9.0
String returns the value and validity of row in string column j. The returned string is a view; the arena swap planned for a later phase keeps this signature. See Chunk.Int64 for the NULL and panic contract.
func (*Chunk) StringColumn ¶ added in v0.9.0
StringColumn returns the backing of string column j. It panics on a kind mismatch.
This []string shape is Phase-1 only: once the string bytes arena lands it will no longer exist. Consumers that must survive that change should use the cell accessor Chunk.String (which keeps its signature).
type ChunkColumnFiller ¶ added in v0.9.0
ChunkColumnFiller extracts one projected column value from a COLUMNAR input row — source row srcRow of src — and appends it to column dstCol of dst, WITHOUT boxing the input node id (the whole point of the chunk-input path: it reads the raw int64 NodeID from src via Chunk.Int64 instead of type-asserting a boxed expr.Value). Like ColumnFiller it MUST append EXACTLY one value to dstCol so the chunk stays rectangular, and falls back — byte-identically — to the row-at-a-time evaluation for any source row it cannot take the unboxed fast path on. It is built by the engine and paired 1:1 with the operator's ColumnFiller fallbacks; the two are equivalent by construction. It is used only when the child is a NodeIDColumnProducer (#1704 P3).
type ChunkPool ¶ added in v0.9.0
type ChunkPool struct {
// contains filtered or unexported fields
}
ChunkPool is a sync.Pool-backed pool of Chunk instances with a fixed schema (column kinds) and capacity. Operators that process a high volume of batches should obtain chunks from a shared pool to reduce GC pressure.
ChunkPool is safe for concurrent use; the Chunk instances it vends are not.
func NewChunkPool ¶ added in v0.9.0
NewChunkPool creates a ChunkPool that vends Chunks with the given capacity and column kinds. The kinds slice is copied, so the caller may reuse it.
func (*ChunkPool) Get ¶ added in v0.9.0
Get retrieves a Chunk from the pool, or allocates a new one. A pooled Chunk was Chunk.Reset before being returned, so it is empty.
type ChunkPredicate ¶ added in v0.9.0
ChunkPredicate decides, WITHOUT boxing, whether source row `row` of `src` passes the filter predicate. It returns (keep, decided): decided=true means keep is authoritative (the predicate was fully evaluated over the unboxed columns); decided=false means the predicate shape cannot be decided unboxed for this row, and the caller must fall back to the boxed row predicate for a byte-identical result. A ChunkPredicate is built by the engine (the cypher package), which owns the graph and the openCypher comparison semantics, and is handed to NewColumnarFilter. It never boxes a scalar; a nil ChunkPredicate means "always fall back" (every row uses the boxed predicate).
type ChunkProducer ¶ added in v0.9.0
type ChunkProducer interface {
Operator
// NewOutputChunk returns a Chunk sized for this operator's output columns.
NewOutputChunk(capacity int) *Chunk
// FillChunk appends up to maxRows more output rows into dst (column-major) and
// returns the number of complete rows appended (0 at end-of-stream). dst must
// have been obtained from NewOutputChunk on the same operator.
FillChunk(dst *Chunk, maxRows int) (int, error)
}
ChunkProducer is implemented by a terminal operator that can emit its output column-major into a Chunk, letting a columnar-aware sink box values only at the API boundary rather than once per cell during the drain (rmp #1704). It is an optional capability discovered by type assertion; an operator that does not implement it is drained row-at-a-time via Operator.Next exactly as before.
func NewColumnarExpand ¶ added in v0.10.0
func NewColumnarExpand(exp *Expand) (ChunkProducer, bool)
NewColumnarExpand presents exp as a ChunkProducer when exp's child is itself a ChunkProducer (so [Expand.fillChunk] can pull it column-major and the chunk chain stays unbroken). It returns (wrapper, true) on success or (nil, false) when the child is row-mode, in which case the caller keeps the plain row-mode exp. The returned wrapper also implements NodeIDColumnProducer.
type ColumnFiller ¶ added in v0.9.0
ColumnFiller extracts one projected column value for the current input row and appends it to column col of dst, WITHOUT boxing a plain scalar into an expr.Value — the whole point of the columnar projection path. It is built by the engine (the cypher package), which owns the graph and the property-value classification, and handed to NewColumnarProject.
A ColumnFiller MUST append EXACTLY one value to dst column col — typed via Chunk.PutInt64/Chunk.PutFloat64/Chunk.PutString/Chunk.PutBool, boxed via Chunk.PutValue, or NULL via Chunk.PutNull — so the chunk stays rectangular. A filler that cannot take the unboxed fast path for the current row (a cell that is not a resolvable node, or a value that must retain special decoding such as a temporal) falls back to the row-at-a-time evaluation and appends the resulting boxed value via Chunk.PutValue, keeping the result byte-identical to the row-at-a-time path.
type ColumnarFilter ¶ added in v0.9.0
type ColumnarFilter struct {
Filter // boxed fallback: promoted Next/Close, predFn, ctx
// contains filtered or unexported fields
}
ColumnarFilter applies a predicate to a columnar input and compacts the passing rows into a column-major output chunk.
The Filter is embedded BY VALUE, not by pointer: a ColumnarFilter is one heap allocation, exactly like the plain Filter it replaces, so building one for a predicate whose parent turns out to consume it row-at-a-time (e.g. an aggregation) costs no extra allocation over the plain Filter. The columnar-only state (scratch batch, cursor) stays zero until the columnar FillChunk path first runs, so the boxed Next path never pays for it either.
ColumnarFilter is NOT safe for concurrent use.
func NewColumnarFilter ¶ added in v0.9.0
func NewColumnarFilter(child ChunkProducer, predFn FilterFn, pred ChunkPredicate) *ColumnarFilter
NewColumnarFilter creates a ColumnarFilter over a ChunkProducer child. predFn is the row-at-a-time predicate (the Operator.Next fallback, identical to NewFilter); pred is the parallel unboxed fast path (nil to always fall back). The two must be equivalent for every row pred decides — see ChunkPredicate.
func (*ColumnarFilter) FillChunk ¶ added in v0.9.0
func (op *ColumnarFilter) FillChunk(dst *Chunk, maxRows int) (int, error)
FillChunk appends up to maxRows PASSING rows into dst (column-major) and returns the number appended, 0 at end-of-stream. It implements ChunkProducer.
It pulls the child in FULL batches into an owned scratch chunk and evaluates the predicate over each source row: the unboxed ChunkPredicate fast path when it can decide, otherwise a one-row box through the SAME boxed predicate the Next path uses (byte-identical). Passing rows are compacted into dst via Chunk.AppendRowFrom with no per-row box.
The scratch cursor (scratchPos) PERSISTS across calls: a single child pull can yield more survivors than the remaining dst capacity, so filling stops mid-batch and resumes here on the next call rather than re-pulling (which would drop or duplicate rows). Consequently a short return (n < maxRows) means the CHILD is exhausted — never merely that one internal pull was selective — which the drain relies on to detect end-of-stream (#1704 P3).
func (*ColumnarFilter) Init ¶ added in v0.9.0
func (op *ColumnarFilter) Init(ctx context.Context) error
Init initialises the embedded Filter (and, through it, the child) and resets the columnar cursor. The scratch batch is allocated lazily on the first ColumnarFilter.FillChunk call, so a ColumnarFilter driven only through Next (a non-columnar parent) allocates nothing beyond the plain Filter.
func (*ColumnarFilter) NewOutputChunk ¶ added in v0.9.0
func (op *ColumnarFilter) NewOutputChunk(capacity int) *Chunk
NewOutputChunk returns a Chunk shaped like the child's output: ColumnarFilter is a row-preserving passthrough (it drops rows, never changes the column layout), so its output schema equals its child's. It implements ChunkProducer.
type ColumnarHashJoin ¶ added in v0.10.0
type ColumnarHashJoin struct {
// contains filtered or unexported fields
}
ColumnarHashJoin is the ChunkProducer equi-join operator described in the file comment. It implements both the row-mode Operator contract (its Next is the byte-identical fallback for a non-columnar parent) and the column-major ChunkProducer contract (FillChunk, preferred by a columnar-aware sink).
ColumnarHashJoin is NOT safe for concurrent use.
func NewColumnarHashJoin ¶ added in v0.10.0
func NewColumnarHashJoin(build, probe Operator, buildFn, probeFn KeyFn, buildOnLeft bool) (*ColumnarHashJoin, bool)
NewColumnarHashJoin returns a ColumnarHashJoin when BOTH build and probe are [NodeIDColumnProducer]s, or (nil, false) otherwise — in which case the caller keeps the row-mode HashJoin (design §6.2: the columnar operator is wired only when every child qualifies, so existing plans are unchanged).
The NodeIDColumnProducer requirement (stronger than plain ChunkProducer) is load-bearing for the allocation win: because every hash-join arm is a bare node scan, both children carry each bound node's raw int64 NodeID unboxed, so the join's output columns are all raw NodeIDs too and ColumnarHashJoin can itself be a NodeIDColumnProducer. That lets a ColumnarProject above the join read a node property (a.k / b.k) unboxed straight from the output chunk, keeping the chunk chain unbroken to the sink (design §0). Gating on plain ChunkProducer instead would break that chain for node-property projection: the projection falls back to row-input Next and re-boxes every output node cell under fan-out — a measured allocation REGRESSION (#2065 pattern). A child that is a ChunkProducer but not a NodeIDColumnProducer therefore keeps the row-mode HashJoin.
- build is drained fully, column-major, into the hash table.
- probe is streamed column-major against the table.
- buildFn / probeFn extract the join key from a build / probe row.
- buildOnLeft selects the output column order (build||probe vs probe||build), matching the Apply the planner replaces exactly as HashJoin does.
ColumnarHashJoin takes ownership of both plans; callers must not use them afterwards.
func (*ColumnarHashJoin) Close ¶ added in v0.10.0
func (op *ColumnarHashJoin) Close() error
Close releases the hash table and closes both child plans.
func (*ColumnarHashJoin) FillChunk ¶ added in v0.10.0
func (op *ColumnarHashJoin) FillChunk(dst *Chunk, maxRows int) (int, error)
FillChunk appends up to maxRows matched output rows into dst column-major and returns the number appended (0 at end-of-stream). Matched build columns are copied from the retained build buffer and probe columns from the current probe batch, both unboxed via Chunk.CopyCellTo. It implements ChunkProducer.
func (*ColumnarHashJoin) Init ¶ added in v0.10.0
func (op *ColumnarHashJoin) Init(ctx context.Context) error
Init initialises both child plans and resets join state.
func (*ColumnarHashJoin) NewOutputChunk ¶ added in v0.10.0
func (op *ColumnarHashJoin) NewOutputChunk(capacity int) *Chunk
NewOutputChunk returns a Chunk sized for the join output: one column per output column, typed by the children's output schema (build||probe or probe||build per buildOnLeft) and pre-sized to capacity. Because both children are NodeIDColumnProducers, every output column has a known scalar kind, so a typed chunk — rather than a dynamic one that reallocates its backing as it fills — copies each cell unboxed via Chunk.CopyCellTo and boxes byte-identically at the sink. It implements ChunkProducer.
func (*ColumnarHashJoin) Next ¶ added in v0.10.0
func (op *ColumnarHashJoin) Next(out *Row) (bool, error)
Next advances the join in row mode, boxing the matched (build||probe or probe||build) columns into a reused output buffer. It is the byte-identical fallback HashJoin.Next provides for a non-columnar parent.
func (*ColumnarHashJoin) PlanChildren ¶ added in v0.11.0
func (op *ColumnarHashJoin) PlanChildren() []Operator
PlanChildren reports the build side first, then the probe side, as HashJoin.PlanChildren does.
ColumnarHashJoin declares its own rather than inheriting: it embeds nothing, holding both sides as ChunkProducer. Rendering it under its own name is how the columnar tier becomes visible in a plan (rmp #2222 AC 1) — the row-mode HashJoin and this one are different operators with different costs, and a reader must be able to tell which ran.
func (*ColumnarHashJoin) PlanDetail ¶ added in v0.11.0
func (op *ColumnarHashJoin) PlanDetail() string
PlanDetail reports the build side of the columnar join, as HashJoin.PlanDetail does for the row-mode one.
func (*ColumnarHashJoin) WithByteBudget ¶ added in v0.10.0
func (op *ColumnarHashJoin) WithByteBudget(maxBytes int64, estimateRow func(Row) int64) *ColumnarHashJoin
WithByteBudget bounds the estimated retained size of the build buffer by maxBytes, returning ErrHashJoinMemoryExceeded when exceeded (#1841). It mirrors HashJoin.WithByteBudget exactly (same estimator, same sentinel), so the columnar and row-mode joins trip at the identical threshold. A non-positive maxBytes or nil estimateRow leaves it unbounded. Returns op for chaining and must be called before Init.
type ColumnarLimit ¶ added in v0.11.0
type ColumnarLimit struct {
Limit // boxed fallback: promoted Next/Close/Init, n, emitted, ctx
// contains filtered or unexported fields
}
ColumnarLimit is a Limit that additionally implements ChunkProducer, keeping the chunk chain unbroken through a LIMIT.
The Limit is embedded BY VALUE, so a ColumnarLimit is one heap allocation exactly like the plain Limit it replaces, and a ColumnarLimit whose parent turns out to consume it row-at-a-time costs nothing extra: the promoted Limit.Next runs unchanged, and `emitted` is the SAME field either way, so the two paths cannot disagree about how many rows have been emitted.
ColumnarLimit is NOT safe for concurrent use.
func NewColumnarLimit ¶ added in v0.11.0
func NewColumnarLimit(lim *Limit) (*ColumnarLimit, bool)
NewColumnarLimit returns a ColumnarLimit over lim when lim's child is a ChunkProducer, and (nil, false) otherwise — in which case the caller keeps the plain Limit, whose behaviour is identical.
lim must not have been initialised yet: the returned operator takes over its state.
func (*ColumnarLimit) FillChunk ¶ added in v0.11.0
func (op *ColumnarLimit) FillChunk(dst *Chunk, maxRows int) (int, error)
FillChunk appends up to maxRows rows into dst (column-major) and returns the number appended, 0 once the limit is reached or the child is exhausted. It implements ChunkProducer.
The clamp is the whole operator: at most `n - emitted` further rows may ever be emitted, so the child is asked for no more than that. Because a consumer treats a short fill (n < maxRows) as end-of-stream, clamping below the requested maxRows also signals exhaustion at exactly the right moment — the limit is reached — and a subsequent call returns 0 regardless.
Rows appended here count towards the SAME `emitted` counter Limit.Next uses, so a plan that drains partly column-major and partly row-at-a-time still emits exactly n rows in total.
func (*ColumnarLimit) NewOutputChunk ¶ added in v0.11.0
func (op *ColumnarLimit) NewOutputChunk(capacity int) *Chunk
NewOutputChunk returns a Chunk shaped like the child's output: LIMIT truncates the row stream and never changes the column layout, so its output schema equals its child's. It implements ChunkProducer.
type ColumnarProject ¶ added in v0.9.0
type ColumnarProject struct {
*Project
// contains filtered or unexported fields
}
ColumnarProject applies a list of scalar-property projections column-major.
ColumnarProject is NOT safe for concurrent use.
func NewColumnarProject ¶ added in v0.9.0
func NewColumnarProject(child Operator, items []ProjectionItem, fillers []ColumnFiller) (*ColumnarProject, error)
NewColumnarProject creates a ColumnarProject. items are the row-at-a-time projection items (the Operator.Next fallback, identical to NewProject); fillers are the parallel columnar extractors, one per item in the same order. len(fillers) must equal len(items).
func (*ColumnarProject) FillChunk ¶ added in v0.9.0
func (op *ColumnarProject) FillChunk(dst *Chunk, maxRows int) (int, error)
FillChunk pulls up to maxRows input rows from the child and appends each as one column-major row into dst via the ColumnFiller extractors, returning the number of complete rows appended (0 at end-of-stream). dst is the caller-owned sink chunk, filled incrementally across calls; box-at-sink happens later at the API boundary. It honours context cancellation before pulling each row. It implements ChunkProducer.
On a filler error the partially-appended row is left in dst but is not counted in the returned n; the caller (the result-materialisation drain) records the error and serves no rows, so the ragged tail is never observed.
func (*ColumnarProject) NewOutputChunk ¶ added in v0.9.0
func (op *ColumnarProject) NewOutputChunk(capacity int) *Chunk
NewOutputChunk returns a Chunk sized for this operator's output: one dynamic column per projection item, so each column's kind is decided by the values the fillers produce (a property column's kind is not known until the values are read). It implements ChunkProducer.
func (*ColumnarProject) WithChunkInput ¶ added in v0.9.0
func (op *ColumnarProject) WithChunkInput(chunkFillers []ChunkColumnFiller) error
WithChunkInput switches this ColumnarProject to consume its child column-major (#1704 P3): the child must be a NodeIDColumnProducer so chunkFillers can read raw int64 NodeID columns unboxed, and len(chunkFillers) must equal the number of projection items. It returns an error otherwise, leaving the operator on the row-input path. Call before Init. Passing this way — rather than a constructor variant — keeps the P2 NewColumnarProject signature and its callers untouched; the chunk-input path is strictly additive and opt-in.
func (*ColumnarProject) WithScalarChunkInput ¶ added in v0.9.0
func (op *ColumnarProject) WithScalarChunkInput(chunkFillers []ChunkColumnFiller) error
WithScalarChunkInput switches this ColumnarProject to consume its child column-major over ALREADY-MATERIALISED scalar columns (#2045): the child must be a ChunkProducer, and each chunk filler copies a source-chunk cell into the output WITHOUT any graph read — the value is already materialised in the child's chunk (a prior columnar projection produced it under the query's visibility barrier). len(chunkFillers) must equal the number of projection items. It returns an error otherwise, leaving the operator on the row-input path.
Unlike ColumnarProject.WithChunkInput it does NOT require a NodeIDColumnProducer: the scalar-passthrough fillers never read the live graph by NodeID, so the box-at-sink isolation contract that forbids a deferred entity-by-id read (rmp #1704 P4/P5) does not apply — every value copied here was captured at query time. Call before Init.
type ConstraintInfo ¶ added in v0.2.0
type ConstraintInfo struct {
// Label is the constrained node label.
Label string
// Property is the constrained property key.
Property string
// Name is the user-defined constraint name (may be empty for a constraint
// registered without one).
Name string
// KindUnique is true for a UNIQUE constraint, false for NOT NULL.
KindUnique bool
}
ConstraintInfo is a structured description of one registered constraint, used to persist the constraint set durably and to re-register it on recovery. KindUnique distinguishes UNIQUE (true) from NOT NULL (false).
type ConstraintKind ¶
type ConstraintKind uint8
ConstraintKind distinguishes UNIQUE from NOT_NULL constraints.
const ( // ConstraintUnique requires that at most one node with a given label has a // particular value for the constrained property. ConstraintUnique ConstraintKind = iota // ConstraintNotNull requires that every node with a given label has a // non-null value for the constrained property. ConstraintNotNull )
type ConstraintRegistry ¶
type ConstraintRegistry struct {
// contains filtered or unexported fields
}
ConstraintRegistry is a thread-safe registry of active constraints. It stores unique and not-null constraints keyed by "label.prop".
ConstraintRegistry is safe for concurrent use.
func NewConstraintRegistry ¶
func NewConstraintRegistry() *ConstraintRegistry
NewConstraintRegistry creates an empty ConstraintRegistry.
func (*ConstraintRegistry) CheckSetProperty ¶
func (r *ConstraintRegistry) CheckSetProperty(ct *ConstraintTxn, labels []string, prop string, value lpg.PropertyValue, mgr *index.Manager) error
CheckSetProperty validates that setting prop = value on a node with the given labels does not violate any registered constraint. mgr is used for unique-constraint index lookups (hash index Cardinality check) as a secondary source; the primary source is the registry's own value set.
The primary value-set is authoritative: when it is present (non-nil) and does not contain value, the secondary hash-index check is skipped. The value-set is seeded from the live graph at constraint creation and kept current by RecordPropertySet / ReleasePropertyValue / ReseedFromGraph, so an absent primary "not present" signal means the value is genuinely free. The secondary check is consulted only when the primary set has not been initialised (nil), covering the narrow window before SeedUniqueValues is called.
Returns *ConstraintViolationError (which wraps ErrConstraintViolation) on the first violation found; nil when all constraints pass.
It CHECKS ONLY, which is not enough for a concurrent writer ¶
This is a read: it takes the read lock, consults the value-set, and returns. Reserving the value happens later and separately, in [RecordPropertySet], which takes the WRITE lock — so between the two, another writer can run the same check and reach the same conclusion. Two writers then both insert and the UNIQUE constraint is violated with both statements reporting success.
That is unreachable while the engine serialises whole write statements on the exclusive visibility barrier, which is why this shape survived. It is NOT reachable-but-rare: with the concurrent write path enabled it was measured at 14 of 15 runs (rmp #2321). A writer that can overlap another must therefore call ConstraintRegistry.ReserveSetProperty, which does both halves under one lock.
This entry point remains for callers that genuinely only want to ASK — pre-validation and diagnostics — and for the existing test surface.
ct carries the caller's own uncommitted releases, so asking about a value the asking transaction has itself given up answers "free"; nil means "no transaction". See ConstraintTxn.
func (*ConstraintRegistry) CommitTxn ¶ added in v0.11.0
func (r *ConstraintRegistry) CommitTxn(ct *ConstraintTxn)
CommitTxn applies ct's pending releases to the shared value-sets and clears it.
It must be called exactly once per transaction that committed, from inside the same window that publishes the transaction's writes, so the value a committed release frees becomes available at the instant the graph stops holding it.
A rolled-back transaction calls ConstraintTxn.Reset instead — there is nothing to undo, because a deferred release never touched anything shared.
Safe for concurrent use.
func (*ConstraintRegistry) Constraints ¶ added in v0.2.0
func (r *ConstraintRegistry) Constraints() []ConstraintInfo
Constraints returns a structured snapshot of every registered constraint, in deterministic order (UNIQUE before NOT NULL, then by label, property, name). It is used to persist the constraint set into a snapshot and to compare the recovered set against the live one.
Constraints is safe for concurrent use.
func (*ConstraintRegistry) Count ¶ added in v0.3.0
func (r *ConstraintRegistry) Count() int
Count returns the number of registered constraints (UNIQUE + NOT NULL). It is a cheap, allocation-free alternative to len(Constraints()) that the engine uses to mirror the count onto the graph for the checkpointer (#1464).
Count is safe for concurrent use.
func (*ConstraintRegistry) HasAnyNotNull ¶ added in v0.6.0
func (r *ConstraintRegistry) HasAnyNotNull() bool
HasAnyNotNull reports whether at least one NOT NULL (property-existence) constraint is registered, WITHOUT taking the registry lock.
It is the cheap gate the engine consults on every statement before doing any touched-node existence-constraint work: when it returns false the commit-time check is a no-op and the per-transaction touched-node recording is skipped entirely.
It used to take the read lock to measure `len(notNull)`, which put one lock acquisition per statement on the write path — cheap while writers were serialised and no longer free once rmp #2306 let them overlap, for the same reason [ConstraintRegistry.uniqueActive] gives: the acquisition IS the cost. It now reads the counter, which is sound for the same reason: a registration needs the schema barrier held exclusively, and an ordinary write holds it shared for its whole bracket.
HasAnyNotNull is safe for concurrent use.
func (*ConstraintRegistry) HasAnyUnique ¶ added in v0.11.0
func (r *ConstraintRegistry) HasAnyUnique() bool
HasAnyUnique reports whether any UNIQUE constraint is registered, WITHOUT taking the registry lock.
It is the write path's gate: with no UNIQUE constraint there is nothing to check and nothing to reserve, so the three value-set methods return before touching mu. See [ConstraintRegistry.uniqueActive] for the measurement that motivated it and for why the lock-free read is sound.
func (*ConstraintRegistry) HasNotNull ¶
func (r *ConstraintRegistry) HasNotNull(label, prop string) bool
HasNotNull reports whether a not-null constraint exists for (label, prop).
func (*ConstraintRegistry) HasUnique ¶ added in v0.2.0
func (r *ConstraintRegistry) HasUnique(label, prop string) bool
HasUnique reports whether a unique constraint exists for (label, prop).
func (*ConstraintRegistry) ListConstraintRows ¶
func (r *ConstraintRegistry) ListConstraintRows() [][]expr.Value
ListConstraintRows returns a [][]expr.Value where each inner slice has four elements: [name, type, label, property]. The name column carries the constraint's declared (or auto-generated) name — the same name DROP CONSTRAINT resolves by — falling back to the canonical "label.prop" key only for a constraint registered without a name (the legacy anonymous path). type is "UNIQUE" or "NOT_NULL". Rows are returned in deterministic order (name, type, label, property).
ListConstraintRows is safe for concurrent use.
func (*ConstraintRegistry) NameInUse ¶ added in v0.8.0
func (r *ConstraintRegistry) NameInUse(name string) (kind ConstraintKind, label, prop string, found bool)
NameInUse reports the identity of the constraint currently holding name, or found=false when no constraint carries it. It is the CREATE-time lookup used to reject a name already used by a different constraint. NameInUse is safe for concurrent use and deterministic (see [ResolveByName]).
func (*ConstraintRegistry) NotNullProperties ¶ added in v0.6.0
func (r *ConstraintRegistry) NotNullProperties(label string) []string
NotNullProperties returns the property keys for which a NOT NULL constraint is registered on label, or nil when label carries no existence constraint. It is the per-label lookup the commit-time existence check uses to test only the constrained properties of a touched node's labels.
The lookup is O(1) via the notNullByLabel index and allocates nothing on this hot path: it returns the registry's own copy-on-write slice, which RegisterNotNull / UnregisterNotNull never mutate in place. The caller must treat the result as READ-ONLY (it is shared and must not be appended to or modified). Most labels carry zero or one existence constraint, so the common return is nil or a one-element slice. NotNullProperties is safe for concurrent use.
func (*ConstraintRegistry) RecordPropertySet ¶
func (r *ConstraintRegistry) RecordPropertySet(labels []string, prop string, value lpg.PropertyValue)
RecordPropertySet records that a property value has been successfully written to a node with the given labels. This keeps the unique value sets up-to-date so that subsequent CheckSetProperty calls detect violations. It is a no-op when no unique constraint exists for (label, prop). # DO NOT CALL THIS FROM A WRITE PATH (rmp #2357/#2358)
Its only legitimate caller is [releaseConstraintValue], where it IS the journaled inverse of a release: a rolled-back release must put the value back.
Every write path must reserve through [reserveConstraintValue] and nothing else. Eleven write sites used to call this immediately AFTER their reserve, and every one of those calls was dead weight: the body below is the SAME set insert as ConstraintRegistry.ReserveSetProperty's phase 2, over the same (labels, prop, value), and a set insert is idempotent — so the value was already present. What it was not free of is r.mu: each call took this registry's WRITE lock a second time per constrained property write, and [nodeStateReader] records that this lock measured 57 % of ALL lock delay at sixteen writers on a schema with no constraints at all. Verified site by site before the calls were removed, including the three whose reserve sits more than a dozen lines above it.
A new write path that "records" what it has already reserved therefore buys nothing and pays a lock acquisition. Reserve, and stop.
func (*ConstraintRegistry) RegisterNotNull ¶
func (r *ConstraintRegistry) RegisterNotNull(label, prop string)
RegisterNotNull adds a not-null constraint for (label, prop).
func (*ConstraintRegistry) RegisterUnique ¶
func (r *ConstraintRegistry) RegisterUnique(label, prop, indexName string)
RegisterUnique adds a unique constraint for (label, prop) backed by indexName in the index.Manager.
func (*ConstraintRegistry) ReleasePropertyValue ¶ added in v0.3.0
func (r *ConstraintRegistry) ReleasePropertyValue(ct *ConstraintTxn, labels []string, prop string, value lpg.PropertyValue)
ReleasePropertyValue removes a value from the unique value-set so it is no longer treated as "in use" by the constraint. It must be called whenever a previously recorded value is no longer present in the graph: on node deletion, on REMOVE of a constrained property, and when SET replaces an existing constrained value with a new one (releasing the old value).
ReleasePropertyValue is a no-op when the value was not present in the value-set, or when no unique constraint exists for (label, prop). It is safe for concurrent use.
It is DEFERRED when a transaction owns it (rmp #2366) ¶
With ct non-nil the release is recorded as that transaction's own pending contribution and the shared value-set is left alone until ConstraintRegistry.CommitTxn. That is what makes a rollback free: it drops a private mark instead of writing a re-reservation into shared state that a peer's COMMITTED release may already have changed. ConstraintTxn carries the interleaving this closes.
With ct nil there is no transaction and therefore nothing to roll back, so the release applies immediately — the correct reading of a change that is committed the instant it is made.
func (*ConstraintRegistry) ReseedFromGraph ¶ added in v0.3.0
func (r *ConstraintRegistry) ReseedFromGraph(scanFn func(label, prop string) []lpg.PropertyValue)
ReseedFromGraph clears every UNIQUE value-set and rebuilds it from the provided (label, prop) → values mapping. It is called after an in-memory transaction undo (rollback) so the registry reflects the restored graph state rather than the rolled-back writes.
The caller supplies a scanFn that, given a label and property key, returns the property values currently carried by all live nodes with that label. ReseedFromGraph acquires the registry write lock only once per constraint, so the scan itself must NOT hold the registry lock.
func (*ConstraintRegistry) ReserveSetProperty ¶ added in v0.11.0
func (r *ConstraintRegistry) ReserveSetProperty(ct *ConstraintTxn, labels []string, prop string, value lpg.PropertyValue, mgr *index.Manager) error
ReserveSetProperty validates that setting prop = value on a node with the given labels violates no registered constraint AND, in the same critical section, records the value as in use — so no concurrent writer can pass the same check.
It is the enforcement entry point for every write path. On success the value is already reserved and the caller's later ConstraintRegistry.RecordPropertySet is a harmless idempotent no-op. On failure NOTHING is reserved, for any label.
Why atomic, and the prior art ¶
Splitting the test from the insert is a check-then-act race, and it is the defect ConstraintRegistry.CheckSetProperty documents above. PostgreSQL closes the same hole by holding the leaf page's buffer lock ACROSS the uniqueness check and the insertion: `_bt_doinsert` calls `_bt_check_unique` with the buffer locked and does not release it before inserting, and `_bt_stepright` states the reason in as many words — "We must write-lock the target page before releasing write lock on current page; else someone else's _bt_check_unique scan could fail to see our insertion" (postgres/postgres, master @ 36f7330, 2026-08-03, src/backend/access/nbtree/nbtinsert.c:1033). Memgraph validates unique constraints under the constraint structure's own lock for the same reason (memgraph/memgraph, branch master, src/storage/v2/constraints/unique_constraints.cpp).
Why a duplicate is a VIOLATION here and not a retriable conflict ¶
PostgreSQL distinguishes two cases: a duplicate committed by a finished transaction is a constraint violation, while a duplicate inserted by a STILL-RUNNING transaction makes the inserter release its lock, wait for that transaction (`XactLockTableWait`, or `SpeculativeInsertionWait` for `INSERT … ON CONFLICT`) and start over — so the outcome depends on whether the peer commits or aborts.
GoGraph cannot make that distinction here, and the reason is structural rather than an oversight: PostgreSQL's index entry carries the inserting transaction id, while this value-set is a set of strings with no owner and the registry has no transaction-liveness oracle to consult even if it had one. So every duplicate is reported as what openCypher calls it — a constraint violation, a client error — which is always correct for a committed duplicate and conservative for an in-flight one (it rejects a statement that a retry might have satisfied, rather than admitting a statement that breaks the invariant). Refining the in-flight case to a retriable [mvcc.ErrSerializationConflict], which is what Memgraph reports, needs the value-set to carry its reserver's transaction id; that arrives with the transaction threading in rmp #2320.
Intra-statement duplicates are now caught too ¶
A statement that writes the same constrained value to two nodes — `SET a.email = 'x', b.email = 'x'` under a UNIQUE constraint on email — used to pass, because every check ran before any record. Reserving at check time rejects the second write. That is a fix, not a side effect: such a statement leaves the graph violating a declared invariant. ct carries the reservations this transaction has released but not committed, so a value it has itself given up is not refused as its own duplicate; see ConstraintTxn. It may be nil, which means "no transaction" — a caller with nothing to roll back.
func (*ConstraintRegistry) ResolveByName ¶ added in v0.4.0
func (r *ConstraintRegistry) ResolveByName(name string) (kind ConstraintKind, label, prop string, found bool)
ResolveByName resolves a user-defined constraint name to its (kind, label, property) identity, so a DROP CONSTRAINT <name> can locate the constraint to remove. It searches the UNIQUE names first, then the NOT NULL names, and returns found=false when no constraint carries that name.
Only constraints registered WITH a name (via [SetConstraintName], which the CREATE CONSTRAINT executor calls) are resolvable; an anonymous constraint registered through the legacy [RegisterUnique] / [RegisterNotNull] path has no name to match.
ResolveByName is safe for concurrent use. It is deterministic: it scans the UNIQUE names then the NOT NULL names, each in sorted key order, and returns the first match, so a lookup never depends on Go map iteration order. Once constraint names are enforced unique at CREATE time (ErrConstraintNameConflict) at most one constraint can match, so the sorted scan is belt-and-braces.
func (*ConstraintRegistry) SeedUniqueValues ¶ added in v0.2.0
func (r *ConstraintRegistry) SeedUniqueValues(label, prop string, values []lpg.PropertyValue) error
SeedUniqueValues populates the value-set of an already-registered UNIQUE constraint on (label, prop) from the property values of the nodes that currently carry the label. It is the post-creation seed that makes a constraint added to a non-empty dataset functional: without it the value-set starts empty and pre-existing duplicates (or duplicates of a pre-existing value) are accepted on the next write.
It also enforces the at-creation invariant (Neo4j semantics, audit gap H2): if two of the supplied values are equal it returns a *ConstraintViolationError wrapping ErrConstraintViolation and seeds nothing, so the caller can reject CREATE CONSTRAINT over already-duplicated data. Null values (the zero PropertyValue) are ignored by a UNIQUE constraint and are skipped.
SeedUniqueValues is a no-op (and returns nil) when no UNIQUE constraint is registered for (label, prop).
func (*ConstraintRegistry) SeedUniqueValuesIgnoringDuplicates ¶ added in v0.2.0
func (r *ConstraintRegistry) SeedUniqueValuesIgnoringDuplicates(label, prop string, values []lpg.PropertyValue)
SeedUniqueValuesIgnoringDuplicates seeds the value-set of an already-registered UNIQUE constraint on (label, prop) from values WITHOUT rejecting pre-existing duplicates. It is the recovery seed: recovery must always succeed so the store is serviceable, and a duplicate that predates the constraint is a historical artefact the live enforcement path still rejects on the next write. Null values are skipped. No-op when no UNIQUE constraint is registered for (label, prop).
func (*ConstraintRegistry) SetConstraintName ¶ added in v0.2.0
func (r *ConstraintRegistry) SetConstraintName(kindUnique bool, label, prop, name string)
SetConstraintName records the user-defined name of the constraint of the given kind on (label, prop), so the constraint round-trips durably with the name the client declared. kindUnique selects UNIQUE (true) vs NOT NULL. A later [UnregisterUnique] / [UnregisterNotNull] clears the matching name.
func (*ConstraintRegistry) UniqueIndexName ¶
func (r *ConstraintRegistry) UniqueIndexName(label, prop string) (string, bool)
UniqueIndexName returns the backing index name for a unique constraint on (label, prop), or ("", false) if none exists.
func (*ConstraintRegistry) UniqueProperties ¶ added in v0.11.0
func (r *ConstraintRegistry) UniqueProperties(label string) []string
UniqueProperties returns the property keys for which a UNIQUE constraint is registered on label, or nil when label carries no uniqueness constraint. It is the per-label lookup the LABEL-set path uses to discover which of a node's properties attaching that label brings under a uniqueness constraint (rmp #2352) — the inverse of the (label, prop) lookup every property-set caller makes, which already knows both halves of the key.
The lookup is O(1) via the uniqueByLabel index and allocates nothing: it returns the registry's own copy-on-write slice, which RegisterUnique / UnregisterUnique never mutate in place. The caller must treat the result as READ-ONLY (it is shared and must not be appended to or modified). Most labels carry zero or one uniqueness constraint, so the common return is nil or a one-element slice.
Callers on the write path MUST gate this behind [HasAnyUnique], which is a lock-free atomic load: UniqueProperties itself takes the registry read lock, and putting that on every label write of an unconstrained schema is precisely the cost [ConstraintRegistry.uniqueActive] exists to avoid.
UniqueProperties is safe for concurrent use.
func (*ConstraintRegistry) UnregisterNotNull ¶
func (r *ConstraintRegistry) UnregisterNotNull(label, prop string)
UnregisterNotNull removes the not-null constraint for (label, prop). No-op if absent.
func (*ConstraintRegistry) UnregisterUnique ¶
func (r *ConstraintRegistry) UnregisterUnique(label, prop string)
UnregisterUnique removes the unique constraint for (label, prop). No-op if absent.
type ConstraintTxn ¶ added in v0.11.0
type ConstraintTxn struct {
// contains filtered or unexported fields
}
ConstraintTxn is one transaction's UNCOMMITTED contribution to the UNIQUE value-sets: the values it has RELEASED and not yet committed.
Why a release is deferred and a reservation is not (rmp #2366) ¶
The two directions are not symmetric, and treating them as if they were is the defect. A RESERVATION is taken eagerly into the shared value-set, and that is correct: it must block every peer immediately, and rolling it back — deleting a value no committed peer can have taken, because it was reserved throughout — cannot disturb anyone. A RELEASE is different. Applied eagerly it hands the value to any peer that asks, and if the releasing transaction then rolls back the value has two holders; so the old code applied it eagerly and journaled the RE-RESERVE as the rollback inverse, which put the value back into SHARED state judged against the rolling-back transaction's OWN view:
T1 REMOVE b:Person releases 'old' (eagerly, shared)
T2 SET b.email = 'new' releases 'old', reserves 'new'
T2 ROLLBACK replays: releases 'new', RE-RESERVES 'old'
T1 COMMIT the label removal stands — no live :Person holds 'old'
CREATE (y:Person {email:'old'}) -> REFUSED, for ever
Both transactions behaved correctly in isolation; the merged state is wrong because a rollback wrote to state a peer's COMMITTED release had already changed. Deferring the release removes the write: a rollback drops a private mark and touches nothing shared, so there is nothing to order against a peer's commit.
The transaction still sees its own release — that is what the mark is for, and it is what lets one transaction free a value and take it again on another node.
The zero value is ready to use and allocates nothing until the first release, so a statement under a schema with no UNIQUE constraint costs nothing at all.
Not safe for concurrent use: it belongs to one transaction, which is driven by one goroutine at a time, exactly like the undo log it travels beside.
func (*ConstraintTxn) Reset ¶ added in v0.11.0
func (t *ConstraintTxn) Reset()
Reset drops every mark, which is what a ROLLBACK does. Nothing shared was touched, so there is nothing to undo.
The inline slots are zeroed rather than merely counted out, so a released value's string does not stay reachable through a recycled adapter; the spill map is kept for reuse, exactly as the undo log keeps its slice.
type ConstraintViolationError ¶
type ConstraintViolationError struct {
// Label is the node label the constraint is defined on.
Label string
// Property is the constrained property key.
Property string
// Kind describes the type of constraint: "UNIQUE" or "NOT NULL".
Kind string
// Detail is an optional human-readable explanation.
Detail string
}
ConstraintViolationError carries structured context about which constraint was violated.
func (*ConstraintViolationError) Error ¶
func (e *ConstraintViolationError) Error() string
Error implements the error interface.
func (*ConstraintViolationError) Unwrap ¶
func (e *ConstraintViolationError) Unwrap() error
Unwrap chains to ErrConstraintViolation so callers can use errors.Is.
type CorrelatedApply ¶
type CorrelatedApply struct {
// contains filtered or unexported fields
}
CorrelatedApply is a Volcano pipeline operator that performs a dependent (correlated) join with the convention that the inner pipeline begins with an Argument leaf re-emitting the outer row. The inner row is forwarded verbatim as the operator's output; no concatenation with the outer row is performed (the outer columns are already present in the inner row).
CorrelatedApply is NOT safe for concurrent use.
func NewCorrelatedApply ¶
func NewCorrelatedApply(outer, inner Operator, arg *Argument) *CorrelatedApply
NewCorrelatedApply creates a CorrelatedApply operator.
- outer is the left (driving) plan.
- inner is the right (sub) plan whose leftmost leaf is the provided arg.
- arg is the Argument node at the inner leaf; CorrelatedApply seeds it before each inner Init call so that the inner pipeline observes the current outer row.
CorrelatedApply takes ownership of both plans. The caller must not use outer or inner directly after calling NewCorrelatedApply.
func (*CorrelatedApply) Close ¶
func (op *CorrelatedApply) Close() error
Close releases resources and closes both the outer and inner plans.
func (*CorrelatedApply) Init ¶
func (op *CorrelatedApply) Init(ctx context.Context) error
Init initialises the outer plan and stores ctx for subsequent Next calls. The inner plan is initialised lazily on the first outer row.
func (*CorrelatedApply) Next ¶
func (op *CorrelatedApply) Next(out *Row) (bool, error)
Next advances the CorrelatedApply operator. The inner row is forwarded verbatim; CorrelatedApply does not concatenate the outer row because the inner row already carries the outer columns (the inner pipeline's leaf Argument re-emitted them).
func (*CorrelatedApply) PlanChildren ¶ added in v0.11.0
func (op *CorrelatedApply) PlanChildren() []Operator
PlanChildren reports the outer input that drives this operator, then the inner sub-plan it re-drives for each outer row.
type CountBuffer ¶ added in v0.10.0
type CountBuffer struct {
// contains filtered or unexported fields
}
CountBuffer collects relationship count-store deltas and dirty markings produced by write operators during a single write transaction. It is the structural twin of IndexBuffer (design docs/count-store-design.md §3.1).
Call EnqueueDelta / MarkDirty for every count-affecting graph mutation. At the transaction boundary:
- Commit: applies the accumulated deltas then the dirty markings to the count store, then resets.
- Rollback: discards everything without touching the store — no undo log is needed because the store is a pure function of graph state and nothing was applied yet.
Both backing slices grow lazily on first use, so a transaction that touches no count cell (for example a bare CREATE (:N) over an edgeless graph) allocates nothing here. CountBuffer is NOT safe for concurrent use.
func (*CountBuffer) Commit ¶ added in v0.10.0
func (b *CountBuffer) Commit(cs *count.Store)
Commit applies all buffered deltas and then all buffered dirty markings to cs, then resets the buffer. Deltas precede dirty markings so a family the deltas just updated exactly is still marked non-exact when the same commit also trips the budget on it. A nil cs is safe: the buffer is discarded without panicking.
Commit must run inside the write visibility barrier (visMu.Lock, after the WAL fsync succeeds) so the count update becomes visible atomically with the graph writes it describes — the durable-then-visible seam the secondary-index fan-out uses.
What that requirement is, and what it is NOT (rmp #2303, MVCC B1) ¶
It is a VISIBILITY requirement, not an ORDERING one, and the distinction is what lets rmp #2304 remove the barrier without redesigning this. A reader must not observe a count that disagrees with the graph it can see; that is atomicity between two structures and it needs whatever mechanism replaces the barrier to publish both together.
It does NOT rest on the barrier imposing a total order across committers. The count store's own ordering basis is COMMUTATIVITY: count.Store.Apply is an additive delta, and count.Store.MarkDirty is a monotone set insert, so any interleaving of two transactions' buffers reaches the same state a serial schedule would.
That was not free, and it was not true when the claim was first made: Apply deleted a cell at zero-or-below, which discarded a transiently-negative cell and lost the decrement that produced it, making the aggregate order-sensitive. It now deletes at exactly zero. See graph/index/count/commutative_test.go for the measurement and the differential.
The intra-transaction order — every delta, THEN every dirty marking — is a property of this buffer and survives concurrency unchanged, because one buffer belongs to one transaction.
func (*CountBuffer) EnqueueDelta ¶ added in v0.10.0
func (b *CountBuffer) EnqueueDelta(d count.Delta)
EnqueueDelta appends a cell increment to the buffer.
func (*CountBuffer) Len ¶ added in v0.10.0
func (b *CountBuffer) Len() int
Len returns the number of deltas plus dirty markings currently buffered.
func (*CountBuffer) MarkDirty ¶ added in v0.10.0
func (b *CountBuffer) MarkDirty(m count.DirtyMark)
MarkDirty appends an X-scoped dirty marking to the buffer.
func (*CountBuffer) NumDeltas ¶ added in v0.10.0
func (b *CountBuffer) NumDeltas() int
NumDeltas returns the number of buffered cell increments (excluding dirty markings). The commit fan-out reads it before CountBuffer.Commit resets the buffer, to attribute a "deltas applied" observability count to the commit (task #2087). It is zero for a transaction that touched no count cell, so the bare-CREATE write path emits nothing.
func (*CountBuffer) Rollback ¶ added in v0.10.0
func (b *CountBuffer) Rollback()
Rollback discards all buffered deltas and dirty markings without applying them.
type CreateConstraintOp ¶
type CreateConstraintOp struct {
// contains filtered or unexported fields
}
CreateConstraintOp is a Volcano DDL operator that registers a constraint.
CreateConstraintOp is NOT safe for concurrent use.
func NewCreateConstraintOp ¶
func NewCreateConstraintOp( name, label, prop string, kind ConstraintKind, ifNotExists bool, mgr *index.Manager, reg *ConstraintRegistry, onSchemaChange func(), ) *CreateConstraintOp
NewCreateConstraintOp creates a CreateConstraintOp. onSchemaChange, when non-nil, is invoked exactly once after the operator successfully registers a new constraint — i.e. NOT when the IF NOT EXISTS branch silently absorbs an already-registered constraint. The Engine wires e.ClearPlanCache as onSchemaChange so cached plans are invalidated after a real schema mutation.
func (*CreateConstraintOp) Close ¶
func (op *CreateConstraintOp) Close() error
Close implements Operator.
func (*CreateConstraintOp) Init ¶
func (op *CreateConstraintOp) Init(ctx context.Context) error
Init implements Operator.
func (*CreateConstraintOp) Next ¶
func (op *CreateConstraintOp) Next(_ *Row) (bool, error)
Next implements Operator. Performs the CREATE CONSTRAINT side effect on the first call, then signals end-of-stream.
func (*CreateConstraintOp) WithBackingIndex ¶ added in v0.3.0
func (op *CreateConstraintOp) WithBackingIndex(sub index.Subscriber) *CreateConstraintOp
WithBackingIndex supplies a pre-built index subscriber to use as the UNIQUE constraint's backing hash index instead of creating a fresh unbound index. The engine passes a bound index (built and backfilled from the live graph) so the index self-maintains from the change fan-out at commit time. Returns op for chaining.
type CreateIndexOp ¶
type CreateIndexOp struct {
// contains filtered or unexported fields
}
CreateIndexOp is a Volcano DDL operator that registers a new secondary index.
CreateIndexOp is NOT safe for concurrent use.
func NewCreateIndexOp ¶
func NewCreateIndexOp( name string, kind IndexKindExec, ifNotExists bool, mgr *index.Manager, onSchemaChange func(), ) *CreateIndexOp
NewCreateIndexOp creates a CreateIndexOp. onSchemaChange, when non-nil, is invoked exactly once after the operator successfully creates a new index in mgr — i.e. NOT when the IF NOT EXISTS branch silently absorbs a duplicate. The Engine wires e.ClearPlanCache as onSchemaChange so cached plans are invalidated after a real schema mutation.
type CreateNode ¶
type CreateNode struct {
// contains filtered or unexported fields
}
CreateNode creates a new graph node per input row, sets its labels and properties, and appends the new NodeID as a new column.
CreateNode is NOT safe for concurrent use.
func NewCreateNode ¶
func NewCreateNode( nodeVar string, labels []string, properties string, child Operator, mutator GraphMutator, ) (*CreateNode, error)
NewCreateNode creates a CreateNode operator.
nodeVar is the variable name bound to the new node (may be empty if the node is not referenced downstream). labels is the ordered list of labels to attach. properties is the opaque literal property-map string (e.g. `{name: "Alice"}`) produced by the IR translator; it is parsed once during construction. mutator is the graph write surface.
func (*CreateNode) Init ¶
func (op *CreateNode) Init(ctx context.Context) error
Init initialises the operator and its child.
The first CreateNode.Init in the process also seeds [globalNodeCounter] past the largest synthetic key currently interned in op.mutator, so that node keys generated in this process cannot collide with keys persisted by an earlier process and replayed during WAL / snapshot recovery. The seed is gated by [globalNodeCounterSeededOnce] so the scan runs at most once per process regardless of how many CreateNode operators are created.
func (*CreateNode) Next ¶
func (op *CreateNode) Next(out *Row) (bool, error)
Next pulls one row from the child, creates a node, and appends the NodeID column. Returns (true, nil) when a row was produced, (false, nil) at end-of-stream, (false, err) on error.
func (*CreateNode) PlanChildren ¶ added in v0.11.0
func (op *CreateNode) PlanChildren() []Operator
PlanChildren reports the input whose rows it creates a node for.
func (*CreateNode) WithConstraints ¶
func (op *CreateNode) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *CreateNode
WithConstraints attaches a ConstraintRegistry and index.Manager to the operator for pre-write enforcement. Both must be non-nil. Returns op for chaining.
func (*CreateNode) WithParams ¶
func (op *CreateNode) WithParams(params map[string]expr.Value) (*CreateNode, error)
WithParams attaches query parameters for $name substitution in property expressions. Re-parses the property map with the supplied params. Returns op for chaining.
func (*CreateNode) WithPropsEvalFn ¶
func (op *CreateNode) WithPropsEvalFn(fn PropsEvalFn) *CreateNode
WithPropsEvalFn attaches a per-row property evaluator. When fn is non-nil it is called on every Next invocation and its results are merged with the statically parsed props (literal values). Dynamic results take precedence over same-keyed literal values, allowing the property map to contain a mix of literals and expression-valued entries.
type CreateRelationship ¶
type CreateRelationship struct {
// contains filtered or unexported fields
}
CreateRelationship creates a new directed edge per input row between two already-bound nodes.
CreateRelationship is NOT safe for concurrent use.
func NewCreateRelationship ¶
func NewCreateRelationship( startVar, endVar, relVar, relType, properties string, schema map[string]int, child Operator, mutator GraphMutator, ) (*CreateRelationship, error)
NewCreateRelationship creates a CreateRelationship operator.
startVar and endVar are the variable names (column indices are looked up in schema) of the source and destination nodes. relVar is the variable name bound to the new relationship (may be empty). relType is the relationship type label. properties is the opaque literal property-map string. schema maps currently bound variable names to their column indices.
func (*CreateRelationship) Close ¶
func (op *CreateRelationship) Close() error
Close closes the child operator.
func (*CreateRelationship) Init ¶
func (op *CreateRelationship) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*CreateRelationship) Next ¶
func (op *CreateRelationship) Next(out *Row) (bool, error)
Next pulls one row from the child, resolves the endpoint NodeIDs, creates the edge, and appends an optional RelationshipValue column.
func (*CreateRelationship) PlanChildren ¶ added in v0.11.0
func (op *CreateRelationship) PlanChildren() []Operator
PlanChildren reports the input whose rows it creates a relationship for.
func (*CreateRelationship) WithParams ¶
func (op *CreateRelationship) WithParams(params map[string]expr.Value) (*CreateRelationship, error)
WithParams re-parses the property map with the supplied query parameters for $name substitution. Returns op for chaining.
func (*CreateRelationship) WithPropsEvalFn ¶
func (op *CreateRelationship) WithPropsEvalFn(fn PropsEvalFn) *CreateRelationship
WithPropsEvalFn attaches a per-row property evaluator. See CreateNode.WithPropsEvalFn.
type DeleteNode ¶
type DeleteNode struct {
// contains filtered or unexported fields
}
DeleteNode deletes an already-bound node (labels + properties stripped) from the graph, provided it has no incident relationships.
DeleteNode is NOT safe for concurrent use.
func NewDeleteNode ¶
func NewDeleteNode( nodeVar string, schema map[string]int, child Operator, mutator GraphMutator, ) *DeleteNode
NewDeleteNode creates a DeleteNode operator.
func (*DeleteNode) Init ¶
func (op *DeleteNode) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*DeleteNode) Next ¶
func (op *DeleteNode) Next(out *Row) (bool, error)
Next pulls one row from the child and deletes the bound node.
func (*DeleteNode) PlanChildren ¶ added in v0.11.0
func (op *DeleteNode) PlanChildren() []Operator
PlanChildren reports the input whose rows name the nodes it deletes.
func (*DeleteNode) WithConstraintRegistry ¶ added in v0.3.0
func (op *DeleteNode) WithConstraintRegistry(reg *ConstraintRegistry) *DeleteNode
WithConstraintRegistry attaches a ConstraintRegistry so DeleteNode releases unique-constraint value reservations when a node is deleted. Returns op for chaining.
func (*DeleteNode) WithRelEndpoints ¶
func (op *DeleteNode) WithRelEndpoints(fn RelEndpointFn) *DeleteNode
WithRelEndpoints attaches a per-row lookup that returns the (srcID, dstID) endpoints of the edge identified by the bare-variable target. When set AND the schema-direct slot holds an IntegerValue (the in-pipeline edge-id encoding emitted by Expand), the operator dispatches to the edge-removal path instead of treating the integer as a NodeID.
func (*DeleteNode) WithTargetEvalFn ¶
func (op *DeleteNode) WithTargetEvalFn(fn TargetEvalFn) *DeleteNode
WithTargetEvalFn attaches a per-row evaluator for non-variable DELETE targets (subscripts, property access, …). When set, the operator resolves the target value via the evaluator instead of the schema lookup keyed by nodeVar.
type DeleteRelationship ¶
type DeleteRelationship struct {
// contains filtered or unexported fields
}
DeleteRelationship removes a directed edge per input row.
DeleteRelationship is NOT safe for concurrent use.
func NewDeleteRelationship ¶
func NewDeleteRelationship( relVar string, schema map[string]int, child Operator, mutator GraphMutator, ) *DeleteRelationship
NewDeleteRelationship creates a DeleteRelationship operator.
func (*DeleteRelationship) Close ¶
func (op *DeleteRelationship) Close() error
Close closes the child operator.
func (*DeleteRelationship) Init ¶
func (op *DeleteRelationship) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*DeleteRelationship) Next ¶
func (op *DeleteRelationship) Next(out *Row) (bool, error)
Next pulls one row from the child and removes the bound relationship.
func (*DeleteRelationship) PlanChildren ¶ added in v0.11.0
func (op *DeleteRelationship) PlanChildren() []Operator
PlanChildren reports the input whose rows name the relationships it deletes.
func (*DeleteRelationship) WithRelCols ¶ added in v0.9.0
func (op *DeleteRelationship) WithRelCols(rc RelCols) *DeleteRelationship
WithRelCols records the row columns that hold the bound relationship's endpoint NodeIDs and its forward-CSR edge position, so Next resolves the bound parallel instance's stable handle and removes the EXACT instance in a multigraph (rmp #2018) rather than the first-match endpoint slot. When unset (or when the position resolves no handle), Next falls back to the endpoint removal. Must be called before the first Next. Returns op for chaining.
type DetachDelete ¶
type DetachDelete struct {
// contains filtered or unexported fields
}
DetachDelete removes all incident edges from a node and then strips the node's labels and properties.
DetachDelete is NOT safe for concurrent use.
func NewDetachDelete ¶
func NewDetachDelete( nodeVar string, schema map[string]int, child Operator, mutator GraphMutator, ) *DetachDelete
NewDetachDelete creates a DetachDelete operator.
func (*DetachDelete) Init ¶
func (op *DetachDelete) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*DetachDelete) Next ¶
func (op *DetachDelete) Next(out *Row) (bool, error)
Next pulls one row from the child, removes all incident edges of the bound node, then strips the node's labels and properties.
func (*DetachDelete) PlanChildren ¶ added in v0.11.0
func (op *DetachDelete) PlanChildren() []Operator
PlanChildren reports the input whose rows name the nodes it detaches and deletes.
func (*DetachDelete) WithConstraintRegistry ¶ added in v0.3.0
func (op *DetachDelete) WithConstraintRegistry(reg *ConstraintRegistry) *DetachDelete
WithConstraintRegistry attaches a ConstraintRegistry so DetachDelete releases unique-constraint value reservations when a node is deleted. Returns op for chaining.
func (*DetachDelete) WithTargetEvalFn ¶
func (op *DetachDelete) WithTargetEvalFn(fn TargetEvalFn) *DetachDelete
WithTargetEvalFn attaches a per-row evaluator for non-variable DETACH DELETE targets (subscripts, property access, …).
type Distinct ¶
type Distinct struct {
// contains filtered or unexported fields
}
Distinct is a streaming Volcano operator that emits each unique row exactly once. It maintains a hash set of seen rows; hash collisions are resolved by full equality checks.
Distinct is NOT safe for concurrent use.
func NewDistinct ¶
NewDistinct creates a Distinct operator.
- child: the upstream operator.
- maxDistinct: upper bound on distinct rows; pass 0 to use DefaultMaxDistinct.
func (*Distinct) Next ¶
Next pulls rows from the child and emits the first occurrence of each unique row. Duplicate rows are silently discarded. Returns ErrDistinctMemoryExceeded if more than maxDistinct distinct rows are encountered.
func (*Distinct) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input it deduplicates.
func (*Distinct) WithByteBudget ¶ added in v0.7.0
WithByteBudget bounds the estimated retained size of the stored distinct rows by maxBytes. It complements the maxDistinct count cap so a few large-valued distinct rows cannot exceed the engine's result-byte budget before the count cap fires (#1841). A non-positive maxBytes or nil estimateRow leaves the byte dimension disabled. Returns op for chaining and must be called before Init.
type DropConstraintOp ¶
type DropConstraintOp struct {
// contains filtered or unexported fields
}
DropConstraintOp is a Volcano DDL operator that deregisters a constraint.
DropConstraintOp is NOT safe for concurrent use.
func NewDropConstraintOp ¶
func NewDropConstraintOp( name, label, prop string, kind ConstraintKind, ifExists bool, mgr *index.Manager, reg *ConstraintRegistry, onSchemaChange func(), ) *DropConstraintOp
NewDropConstraintOp creates a DropConstraintOp. onSchemaChange, when non-nil, is invoked exactly once after the operator successfully removes a constraint — i.e. NOT when the IF EXISTS branch silently absorbs an absent-constraint condition. The Engine wires e.ClearPlanCache as onSchemaChange so cached plans are invalidated after a real schema mutation.
func (*DropConstraintOp) Close ¶
func (op *DropConstraintOp) Close() error
Close implements Operator.
type DropIndexOp ¶
type DropIndexOp struct {
// contains filtered or unexported fields
}
DropIndexOp is a Volcano DDL operator that deregisters a secondary index.
DropIndexOp is NOT safe for concurrent use.
func NewDropIndexOp ¶
func NewDropIndexOp( name string, ifExists bool, mgr *index.Manager, onSchemaChange func(), ) *DropIndexOp
NewDropIndexOp creates a DropIndexOp. onSchemaChange, when non-nil, is invoked exactly once after the operator successfully drops the index — i.e. NOT when the IF EXISTS branch silently absorbs a missing-index error. The Engine wires e.ClearPlanCache as onSchemaChange so cached plans are invalidated after a real schema mutation.
type Eager ¶
type Eager struct {
// contains filtered or unexported fields
}
Eager is a pipeline-breaking barrier. Init drains every row from the child into an internal buffer; Next then re-emits the buffered rows in insertion order.
Eager is NOT safe for concurrent use: it holds unsynchronised buffer and cursor state mutated across Init/Next/Close, so a single goroutine must drive one operator tree, like every other Operator.
func NewEager ¶
NewEager wraps child in an Eager barrier.
- child: the upstream operator to drain.
- maxRows: upper bound on rows held in memory; pass 0 to use DefaultMaxEagerRows.
func (*Eager) Init ¶
Init initialises the operator AND drains every child row into the buffer so the downstream pipeline can apply LIMIT / SKIP / DISTINCT without starving any write-side child of its driving rows.
The drain is bounded by maxRows (ErrEagerMemoryExceeded when exceeded) and honours context cancellation: a cancelled or expired ctx aborts the drain promptly rather than buffering the entire child first.
func (*Eager) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input it fully materialises before emitting.
func (*Eager) WithByteBudget ¶ added in v0.7.0
WithByteBudget bounds the estimated retained size of the buffered rows by maxBytes. It complements the maxRows count cap so a few large-valued rows cannot exceed the engine's result-byte budget before the count cap fires (#1841). A non-positive maxBytes or nil estimateRow leaves the byte dimension disabled. Returns op for chaining and must be called before Init.
type EagerAggregation ¶
type EagerAggregation struct {
// contains filtered or unexported fields
}
EagerAggregation is a blocking (pipeline-breaking) Volcano operator that groups rows from its child by the specified key columns and applies per-group aggregators. It emits one output row per group once the child is exhausted.
EagerAggregation is NOT safe for concurrent use.
func NewEagerAggregation ¶
func NewEagerAggregation( child Operator, keyCols []int, aggFactories []funcs.AggregatorFactory, maxGroups int, ) (*EagerAggregation, error)
NewEagerAggregation creates an EagerAggregation operator.
- child: the upstream operator to consume.
- keyCols: column indices whose values define the group key. An empty slice computes a single global aggregate.
- aggFactories: one AggregatorFactory per aggregate expression. Must not be empty.
- maxGroups: upper bound on distinct groups; pass 0 to use DefaultMaxGroups.
func (*EagerAggregation) Close ¶
func (op *EagerAggregation) Close() error
Close closes the child operator and releases internal state.
func (*EagerAggregation) Init ¶
func (op *EagerAggregation) Init(ctx context.Context) error
Init initialises the operator. The blocking consume phase is deferred to the first Next call.
func (*EagerAggregation) Next ¶
func (op *EagerAggregation) Next(out *Row) (bool, error)
Next emits the next aggregated row. On the first call it consumes all rows from the child (pipeline breaker) and builds the group table. Subsequent calls iterate through the completed groups.
func (*EagerAggregation) PlanChildren ¶ added in v0.11.0
func (op *EagerAggregation) PlanChildren() []Operator
PlanChildren reports the input it groups and aggregates.
func (*EagerAggregation) WithByteBudget ¶ added in v0.7.0
func (op *EagerAggregation) WithByteBudget(maxBytes int64, estimateRow func(Row) int64) *EagerAggregation
WithByteBudget bounds the estimated retained size of the group keys by maxBytes. It complements the maxGroups count cap so a few large-valued group keys cannot exceed the engine's result-byte budget before the count cap fires (#1841). A non-positive maxBytes or nil estimateRow leaves the byte dimension disabled. Returns op for chaining and must be called before Init.
func (*EagerAggregation) WithChunkInput ¶ added in v0.9.0
func (op *EagerAggregation) WithChunkInput() error
WithChunkInput switches the consume phase to pull the child column-major so the scalar grouping keys are hashed and compared UNBOXED, boxing a key only on new-group creation (#2049), and each aggregate argument column is scatter-accumulated UNBOXED by its kernel. The child must be a ChunkProducer; the grouping keys must occupy chunk columns 0..len(keyCols)-1 and the aggregate arguments the columns after them (the layout the aggregation pre-projection installs), which is the same layout the row-input consume assumes. It returns an error otherwise, leaving the operator on the byte-identical row-input path. Call before Init.
len(keyCols) == 0 is supported: a group-key-free (global) aggregate forms exactly one group, because a zero-column key hashes to the same constant and compares equal for every row, and the empty-input neutral row is synthesised by the consume phase itself (#2185).
The path is reversible and self-checking per batch: a grouping-key column that is not an unboxed scalar backing (a promoted/boxed or non-scalar column) is read via Chunk.BoxCell and hashed/compared through the same boxed equivalence as the row path, so a heterogeneous or non-scalar key stays byte-identical.
type EffectCountingSuppressor ¶ added in v0.11.0
type EffectCountingSuppressor interface {
// SuppressEffectCounting stops (true) or resumes (false) write-effect counting.
// Calls do not nest: a paired true/false around one teardown span is the contract.
SuppressEffectCounting(on bool)
}
EffectCountingSuppressor is implemented by a write adapter whose write-effect counting can be paused for a span of internal teardown (#2212).
It exists because deleting a node is implemented as: strip its incident edges, strip its labels, strip its properties, then tombstone it. Those strips go through the same RemoveNodeLabel / DelNodeProperty calls a user's REMOVE does, but they are NOT user-visible side effects — openCypher declares `DELETE n` and `DETACH DELETE n` as `-nodes 1` and nothing else (cypher/tck/features/clauses/delete/Delete1.feature scenarios [1] and [2]); a deleted node's labels and properties vanish WITH the node rather than being separately removed. Counting them would report effects the spec says did not happen.
It is a separate optional interface rather than a GraphMutator method so the many test stubs implementing GraphMutator need no change; a mutator that does not implement it simply cannot suppress, which is safe because only the counting adapters do.
type Expand ¶
type Expand struct {
// contains filtered or unexported fields
}
Expand is a Volcano pipeline operator that, for each input row, expands one hop along the graph's CSR adjacency.
Expand is NOT safe for concurrent use.
func NewExpand ¶
func NewExpand(input Operator, src AdjacencySource, cfg ExpandConfig) *Expand
NewExpand creates an Expand operator.
src yields the forward and reverse adjacency (the reverse is required for DirIn/DirBoth and ignored for DirOut) together with the type filter keyed to them. It is consulted in Expand.Init, not here.
func NewExpandWithOptions ¶
func NewExpandWithOptions(input Operator, src AdjacencySource, cfg ExpandConfig, options ...expandOption) *Expand
NewExpandWithOptions creates an Expand operator with optional extra configuration applied via functional options (e.g. WithCyphermorphism).
All ExpandConfig fields behave identically to NewExpand. Options are applied after base construction and augment the operator's behaviour without altering its public type.
func (*Expand) Next ¶
Next emits the next (srcID, edgeID, dstID) triplet appended to the current input row. It pulls a new input row whenever the current source's adjacency is exhausted.
func (*Expand) PlanChildren ¶ added in v0.11.0
PlanChildren reports the operator whose rows it expands from.
func (*Expand) PlanDetail ¶ added in v0.11.0
PlanDetail reports whether this hop's destination is already bound and, when it is, which access path it takes to reach it (#2149). This is exactly the kind of physical decision the type name cannot carry: a bound-destination hop is still an *Expand, but it either SEEKS the destination's contiguous run in the destination-ordered CSR — O(log d + r) — or walks the whole neighbour run and filters, which is Θ(d). The two differ by an asymptotic factor on the shape behind triangles, cycle closing and mutual-relationship detection, so a plan that did not distinguish them would hide the change this operator exists to make.
"ExpandInto" is the name openCypher implementations conventionally give this access path; it appears in the DETAIL rather than as the operator name because a rendered name is the concrete Go type and must stay incapable of disagreeing with the operator that runs (rmp #2222). An ordinary hop returns "" and renders as "Expand" alone.
func (*Expand) WithExpandInto ¶ added in v0.11.0
WithExpandInto binds this hop's destination to an already-bound input column, so the operator emits only edges landing on that node instead of one row per neighbour (#2206). col < 0 disables it. Returns op for chaining; call before Init.
func (*Expand) WithExpandIntoSeek ¶ added in v0.11.0
WithExpandIntoSeek enables or disables the O(log d) SEEK for an expand-into hop (#2149). It has no effect unless Expand.WithExpandInto has bound a column.
Disabling it keeps the expand-into FILTER and returns the operator to walking the whole neighbour run, which is the pre-#2149 behaviour and the "off" arm of the differential test that proves the two agree row for row. Returns op for chaining; call before Init.
type ExpandConfig ¶
type ExpandConfig struct {
// MultiplicityFn returns the Cypher CREATE-call multiplicity recorded
// for the directed edge (srcID, dstID). When the returned count is N >
// 1, the operator emits the corresponding output row N times in a row,
// reflecting the openCypher rule that `MATCH ()-[r]->()` enumerates
// each CREATE call separately even when the underlying simple-graph
// storage collapsed them to one entry (Merge5 [21]). A nil fn (or
// returning 0 / 1) disables the multiplicity emit and behaves like a
// plain single-row Expand.
MultiplicityFn func(srcID, dstID uint64) int64
// EdgeType, when non-empty, restricts emitted edges to those whose
// positional index is present in the [AdjacencySource]'s filter with this
// type label.
EdgeType string
// RelCols lists the input-row columns holding edge IDs already traversed
// by sibling Expand operators in the same MATCH pattern. Each emitted
// edge must NOT match any of these columns (openCypher 9 §3.2.2
// relationship-isomorphism / cyphermorphism). Empty disables the
// check.
RelCols []int
// InputCol is the column index in each input row that holds the source
// NodeID (as expr.IntegerValue). Defaults to 0.
InputCol int
// Direction to follow. Defaults to DirOut when zero.
Direction Direction
}
ExpandConfig carries the optional configuration for NewExpand.
The relationship-type FILTER is deliberately not here: it is keyed to the absolute edge positions of one particular adjacency, so it is supplied by the AdjacencySource that yields that adjacency and cannot drift from it (rmp #2317). It used to be a field, resolved at plan-build time alongside a pair that is now resolved at execution time — two lifetimes for two halves of one answer.
type ExpandIntersect ¶ added in v0.11.0
type ExpandIntersect struct {
// contains filtered or unexported fields
}
ExpandIntersect fuses a cycle's open middle hop and its closing seek into one operator driven by a sorted-set intersection.
func NewExpandIntersect ¶ added in v0.11.0
func NewExpandIntersect(input Operator, src IntersectAdjacencySource, cfg *ExpandIntersectConfig) *ExpandIntersect
NewExpandIntersect creates a fused cyclic expand over the forward and reverse CSRs. rev is required: the candidate set is N_in(a).
cfg is taken by pointer because it is 88 bytes and this is a plan-build call, so there is nothing to gain from copying it. A nil cfg is treated as the zero value, which is an untyped fusion reading b from column 0 and a from column 0 — valid but not useful, so callers always pass one.
func (*ExpandIntersect) Close ¶ added in v0.11.0
func (op *ExpandIntersect) Close() error
Close releases the child and drops the snapshots.
func (*ExpandIntersect) Init ¶ added in v0.11.0
func (op *ExpandIntersect) Init(ctx context.Context) error
Init initialises the child, snapshots both CSR directions, and RESETS every cursor so the operator can be re-executed.
Resetting is not housekeeping — omitting it was a real defect, caught by the OPTIONAL MATCH case in cypher/cyclic_intersect_diff_test.go. Under a correlated Apply, Init runs once per OUTER ROW, not once per query. Without the reset the first outer row ran the operator to exhaustion, left done=true, and every subsequent outer row silently produced nothing — so an OPTIONAL MATCH over a cyclic pattern returned a null row for every input except at most the first. It failed silently and returned WRONG RESULTS rather than an error, which is why the reset is stated here explicitly rather than left implicit.
func (*ExpandIntersect) Next ¶ added in v0.11.0
func (op *ExpandIntersect) Next(out *Row) (bool, error)
Next advances by one row.
func (*ExpandIntersect) PlanChildren ¶ added in v0.11.0
func (op *ExpandIntersect) PlanChildren() []Operator
PlanChildren reports the input whose rows drive the intersection.
func (*ExpandIntersect) PlanDetail ¶ added in v0.11.0
func (op *ExpandIntersect) PlanDetail() string
PlanDetail names the two legs' types, which is the part of the physical decision the operator's own name cannot carry.
type ExpandIntersectConfig ¶ added in v0.11.0
type ExpandIntersectConfig struct {
// The two type filters are NOT here (rmp #2317). They map forward edge
// positions to the accepted type set for the middle (b→c) and closing (c→a)
// legs, so they are keyed to one particular adjacency and travel with it; see
// [IntersectAdjacencySource].
//
// MidEdgeType and EndEdgeType, when non-empty, restrict each leg to edges
// present in the corresponding filter.
MidEdgeType string
EndEdgeType string
// RelCols lists input-row columns holding edge IDs already traversed by
// sibling relationship patterns in the same MATCH clause. Both emitted edges
// must avoid all of them (relationship isomorphism). Empty disables the check.
RelCols []int
// MidCol is the input-row column holding b, the middle hop's source.
MidCol int
// EndCol is the input-row column holding a, the node the cycle closes on.
EndCol int
}
ExpandIntersectConfig configures a fused cyclic expand.
type ExprValueEvalFn ¶ added in v0.9.0
ExprValueEvalFn is a per-row evaluator for a whole map-valued RHS expression on a `SET x = <expr>` / `SET x += <expr>` operator. It returns the evaluated value; the operator dispatches on its runtime kind. See SetAllProperties.WithExprEvalFn.
type Filter ¶
type Filter struct {
// contains filtered or unexported fields
}
Filter is a Volcano pipeline operator that applies a FilterFn predicate to each row produced by its child operator. It emits only rows for which the predicate returns expr.BoolValue(true).
Null and false both suppress the row (three-valued logic).
Filter is NOT safe for concurrent use.
func NewFilter ¶
NewFilter creates a Filter operator that wraps child and applies predFn to every row.
func (*Filter) Next ¶
Next advances to the next row that passes the predicate. It pulls rows from the child until one satisfies the predicate, end-of-stream, or an error.
func (*Filter) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input it filters.
type FilterFn ¶
FilterFn is a predicate over a Row. It must return (BoolValue(true), nil) to accept the row. Any other non-error return value (including NULL and BoolValue(false)) causes the row to be dropped. An error halts the pipeline.
type Float64RangeIndex ¶ added in v0.6.0
type Float64RangeIndex struct {
// contains filtered or unexported fields
}
Float64RangeIndex adapts btree.Index[float64] to the [rangeLookup] interface for the UNIFIED numeric range seek (#1652). It backs a CREATE INDEX (btree) companion that indexes BOTH integer- and float-valued nodes under one float64 total order (openCypher orders integers and floats in a single numeric order), so a numeric range bound is a SUPERSET-complete probe over every numeric node — never a non-superset the way an int64-only index would be (it would silently drop the float-valued matches).
Both an integer and a float bound are coerced to float64: a Cypher IntegerValue bound and a FloatValue bound address the same numeric key space. The float64 widening of a large int64 bound can lose precision and make the probe over-return at the boundary, but the engine ALWAYS retains the original AST predicate as a residual Filter on top of the range scan, so any false positive is removed and the result is identical to a label scan+filter (cypher-expert-consultant, #1652). Nil / non-numeric bounds are treated as ±∞ so an unbounded side returns the whole numeric population.
NaN is never a bound here (the extractor only admits finite numeric literals/parameters) and is never indexed (projectNumericPropValue excludes it), so a NaN node can neither be a key nor be returned: the btree's total order places NaN below every real value and Range with a non-NaN lower bound never returns it, and the residual Filter is the final backstop.
func NewFloat64RangeIndex ¶ added in v0.6.0
func NewFloat64RangeIndex(idx interface {
Range(lo, hi float64) *roaring64.Bitmap
}) *Float64RangeIndex
NewFloat64RangeIndex constructs a Float64RangeIndex.
func (*Float64RangeIndex) RangeBitmap ¶ added in v0.6.0
func (r *Float64RangeIndex) RangeBitmap(lo, hi expr.Value) *roaring64.Bitmap
RangeBitmap implements [rangeLookup]. A nil or NULL bound, or one that is neither an integer nor a float, is treated as the corresponding numeric infinity so an unbounded (or undecodable) side spans the whole numeric range — the residual Filter then enforces the exact predicate.
type Foreach ¶ added in v0.9.0
type Foreach struct {
// contains filtered or unexported fields
}
Foreach runs a correlated body sub-plan per outer row for its writes, then passes the outer row through unchanged.
Foreach is NOT safe for concurrent use.
func NewForeach ¶ added in v0.9.0
NewForeach creates a Foreach operator.
- outer is the driving (left) plan.
- inner is the body sub-plan whose leftmost leaf is the provided arg (Argument → Unwind(list) → updating operators).
- arg is the Argument leaf; Foreach seeds it with the current outer row before each body Init so the body observes that row and the loop element.
Foreach takes ownership of both plans.
func (*Foreach) Init ¶ added in v0.9.0
Init initialises the outer plan and stores ctx. The body plan is initialised per outer row inside Next.
func (*Foreach) Next ¶ added in v0.9.0
Next pulls the next outer row, drives the body sub-plan over the list for its side-effects, and emits the outer row unchanged. Returns (false, nil) once the outer plan is exhausted.
func (*Foreach) PlanChildren ¶ added in v0.11.0
PlanChildren reports the outer input that drives this operator, then the inner sub-plan it re-drives for each outer row.
type GlobalAggregateAdapter ¶
type GlobalAggregateAdapter struct {
// contains filtered or unexported fields
}
GlobalAggregateAdapter wraps an EagerAggregation operator that has no group-by keys and ensures the output stream contains exactly one row even when the child is empty.
GlobalAggregateAdapter is NOT safe for concurrent use.
func NewGlobalAggregateAdapter ¶
func NewGlobalAggregateAdapter(child Operator, aggFactories []funcs.AggregatorFactory) *GlobalAggregateAdapter
NewGlobalAggregateAdapter returns a GlobalAggregateAdapter that wraps child. aggFactories must contain one factory per aggregate column, in the same order as the columns produced by child. The factories are invoked exactly once, on empty input, to synthesise the neutral row.
Note: the adapter does not validate that child is a group-by-less EagerAggregation; callers are responsible for that. Wrapping a grouped aggregation has no effect because grouped aggregations always emit either zero (no input) or N rows (one per group), and the empty-input case for a grouped aggregation is correct as is.
func (*GlobalAggregateAdapter) Close ¶
func (op *GlobalAggregateAdapter) Close() error
Close releases the adapter's child.
func (*GlobalAggregateAdapter) Init ¶
func (op *GlobalAggregateAdapter) Init(ctx context.Context) error
Init initialises the adapter and its child.
func (*GlobalAggregateAdapter) Next ¶
func (op *GlobalAggregateAdapter) Next(out *Row) (bool, error)
Next forwards child rows unchanged. When the child reports end-of-stream without ever producing a row, Next emits exactly one synthetic row built from the neutral results of each aggregate factory before signalling exhaustion itself.
func (*GlobalAggregateAdapter) PlanChildren ¶ added in v0.11.0
func (op *GlobalAggregateAdapter) PlanChildren() []Operator
PlanChildren reports the aggregation it adapts to a single global group.
type GraphMutator ¶
type GraphMutator interface {
// AddNode interns n and returns its stable NodeID. Returns the
// error from the underlying graph implementation (currently only
// [adjlist.ErrShardFull] is reachable, and only when the
// underlying [adjlist.Config.MaxShardCapacity] is set).
AddNode(n string) (graph.NodeID, error)
// AddEdge inserts a directed edge (src→dst) with weight w, interning
// endpoints as needed. Returns the stable NodeIDs of src and dst and
// any error from the underlying graph implementation.
AddEdge(src, dst string, w float64) (srcID, dstID graph.NodeID, err error)
// AddEdgeH is [GraphMutator.AddEdge] with a stable per-edge handle
// allocated by the underlying graph and stamped onto the adjacency
// slot. The returned handle keys the *ByHandle metadata setters below
// so a parallel CREATE's type and properties are resolvable on the
// read path by an identity that survives sibling-edge deletion. Used
// by CreateRelationship and by MergeRelationship's create path (rmp
// #1683) so a MERGE-built parallel edge of a distinct type keeps its
// own per-instance identity instead of collapsing onto the per-pair
// label union. The handle is always non-zero on success.
AddEdgeH(src, dst string, w float64) (srcID, dstID graph.NodeID, handle uint64, err error)
// RemoveEdge removes the directed edge from src to dst (no-op if absent).
// In a multigraph it removes the FIRST src→dst adjacency slot regardless of
// which parallel instance is intended; callers that hold a bound instance's
// stable handle must use RemoveEdgeByHandle for instance precision.
RemoveEdge(src, dst string)
// RemoveEdgeByHandle removes the single parallel edge instance identified by
// the stable handle on the (src, dst) pair — its adjacency slot AND its
// per-handle label/property metadata — leaving sibling instances intact.
// Used by DELETE of a specifically-bound relationship so a parallel edge of
// a distinct type/property is retired EXACTLY, not by first-match (rmp
// #2018). handle must be non-zero; a caller with no resolvable handle (simple
// graph, or a post-projection binding that lost the edge position) uses
// RemoveEdge instead. No-op when no slot carries handle.
RemoveEdgeByHandle(src, dst string, handle uint64)
// SetNodeLabel attaches label to n (inserting n if absent). Returns
// any error from the underlying [adjlist.AdjList.AddNode] (see
// [GraphMutator.AddNode]).
SetNodeLabel(n, label string) error
// RemoveNodeLabel detaches label from n (no-op if absent).
RemoveNodeLabel(n, label string)
// RemoveNode tombstones n in the underlying graph so subsequent reads
// (AllNodesScan, count(*), Order) treat the node as absent. Callers
// should strip labels/properties/incident edges before invoking
// RemoveNode so the tombstone reflects the fully-deleted state.
RemoveNode(n string)
// IsTombstoned reports whether the NodeID has been tombstoned. Used by
// AllNodesScan to skip phantom entries the Mapper still indexes.
IsTombstoned(id graph.NodeID) bool
// SetNodeProperty sets the named property on n. Returns any error
// from the underlying [adjlist.AdjList.AddNode] (see
// [GraphMutator.AddNode]).
SetNodeProperty(n, key string, value lpg.PropertyValue) error
// DelNodeProperty removes the named property from n (no-op if absent).
DelNodeProperty(n, key string)
// NodeProperties returns a snapshot of all properties currently on n.
NodeProperties(n string) map[string]lpg.PropertyValue
// NodeLabels returns a snapshot of all labels currently on n in
// unspecified order.
NodeLabels(n string) []string
// HasEdge reports whether a directed edge from src to dst is present.
HasEdge(src, dst string) bool
// SetEdgeLabel attaches label to the directed edge (src, dst).
SetEdgeLabel(src, dst, label string)
// SetEdgeProperty sets the named property on the directed edge (src, dst).
// Returns any error from the underlying graph (e.g. schema violation).
SetEdgeProperty(src, dst, key string, value lpg.PropertyValue) error
// DelEdgeProperty removes the named property from the directed edge
// (src, dst) (no-op if absent).
DelEdgeProperty(src, dst, key string)
// EdgeProperties returns a snapshot of every property currently set on
// the directed edge (src, dst). Returns an empty map when the edge has
// no properties or does not exist.
EdgeProperties(src, dst string) map[string]lpg.PropertyValue
// EdgeLabels returns a snapshot of every label currently attached to
// the directed edge (src, dst). Returns an empty slice when the edge
// has no labels or does not exist. Used by DELETE r to capture the
// relationship type before tombstoning the edge so the row's
// post-delete RelationshipValue keeps `RETURN type(r)` working.
EdgeLabels(src, dst string) []string
// IncEdgeCreateCount bumps the Cypher CREATE-call multiplicity
// counter for the directed edge (src, dst) by one and returns the
// new (1-based) value. The counter records how many CREATE
// statements have targeted the same endpoint pair regardless of
// whether the underlying storage already had an entry — MERGE
// consults it to emit multiplicity rows when an existing edge
// satisfies the merge pattern (Merge5 [3]). The returned index is
// the per-instance idx callers pass to the *At family of
// metadata-write helpers.
IncEdgeCreateCount(src, dst string) int64
// EdgeCreateCount returns the current CREATE-call multiplicity
// counter for the directed edge (src, dst), or 0 when no CREATE
// has been recorded.
EdgeCreateCount(src, dst string) int64
// DecEdgeCreateCount decrements the CREATE-call multiplicity
// counter (floor 0). Called by DELETE so subsequent MERGEs see
// the correct multiplicity.
DecEdgeCreateCount(src, dst string)
// SetEdgeLabelAt attaches `label` to the directed edge instance
// (src, dst) at the supplied 1-based CREATE index. Used by
// CreateRelationship so parallel CREATEs of the same endpoint
// pair retain their distinct labels (Match2 [6] / Match7 [29]).
SetEdgeLabelAt(src, dst string, idx int64, label string)
// EdgeLabelsAt returns the labels recorded at instance `idx` of
// the directed edge (src, dst), or nil when the instance has no
// per-CREATE labels.
EdgeLabelsAt(src, dst string, idx int64) []string
// SetEdgePropertyAt records `key`=`value` on the directed edge
// instance (src, dst) at the supplied 1-based CREATE index. Returns
// any error returned by the installed SchemaValidator.
SetEdgePropertyAt(src, dst string, idx int64, key string, value lpg.PropertyValue) error
// EdgePropertiesAt returns the property map recorded at instance
// `idx` of the directed edge (src, dst), or nil when no
// per-CREATE map was captured.
EdgePropertiesAt(src, dst string, idx int64) map[string]lpg.PropertyValue
// RemoveEdgeInstance drops every per-CREATE label and property
// associated with (src, dst) at `idx`. Used by DELETE to discard
// a specific logical edge while leaving sibling instances
// untouched.
RemoveEdgeInstance(src, dst string, idx int64)
// SetEdgeLabelByHandle attaches `label` to the edge identified by the
// stable `handle` on the (src, dst) pair (see [GraphMutator.AddEdgeH]).
// The handle-keyed analogue of SetEdgeLabelAt; the read path resolves a
// parallel CREATE's type by this identity instead of a positional CSR
// index. No-op when handle is 0.
SetEdgeLabelByHandle(src, dst string, handle uint64, label string)
// EdgeLabelsByHandle returns the labels recorded for the edge
// identified by `handle` on the (src, dst) pair, or nil when none.
EdgeLabelsByHandle(src, dst string, handle uint64) []string
// SetEdgePropertyByHandle records `key`=`value` on the edge identified
// by the stable `handle` on the (src, dst) pair. No-op when handle is 0.
// Returns any error returned by the installed SchemaValidator.
SetEdgePropertyByHandle(src, dst string, handle uint64, key string, value lpg.PropertyValue) error
// DelEdgePropertyByHandle removes exactly `key` from the property bag of
// the edge identified by the stable `handle` on the (src, dst) pair,
// leaving sibling handles untouched. No-op when handle is 0 or the handle
// never carried the key. The single-key removal analogue of
// RemoveEdgeInstanceByHandle (which drops ALL of a handle's metadata); used
// by REMOVE r.x / SET r.x = null on one parallel relationship instance.
DelEdgePropertyByHandle(src, dst string, handle uint64, key string)
// EdgePropertiesByHandle returns the property map recorded for the edge
// identified by `handle` on the (src, dst) pair, or nil when none.
EdgePropertiesByHandle(src, dst string, handle uint64) map[string]lpg.PropertyValue
// RemoveEdgeInstanceByHandle drops every per-handle label and property
// associated with (src, dst) at `handle`. Used by DELETE to discard a
// specific logical edge while leaving sibling handles untouched.
RemoveEdgeInstanceByHandle(src, dst string, handle uint64)
// FirstEdgeHandle returns the stable per-edge handle stamped on the FIRST
// adjacency slot from src to dst, and whether such a handled slot exists.
// It is the by-PAIR handle resolver: MERGE binds a single logical
// (src, dst) edge rather than a specific parallel instance, so the
// ON MATCH / ON CREATE action path uses it to mirror its per-pair property
// writes onto the matched edge's by-handle store. Returns 0 / false when
// either endpoint is unknown, no src→dst edge exists, or the matched slot
// carries the 0 "no handle" sentinel (simple-graph / pre-handle storage) —
// in which case the caller mutates the per-pair store only and never a
// by-handle instance.
FirstEdgeHandle(src, dst string) (uint64, bool)
// OutNeighbours returns the outgoing neighbour node keys of n as a
// snapshot slice. Callers must not mutate the returned slice.
OutNeighbours(n string) []string
// InNeighbours returns the incoming neighbour node keys of n as a
// snapshot slice. This requires a full graph walk for directed adjacency
// lists that do not maintain a reverse index. Callers must not mutate the
// returned slice.
InNeighbours(n string) []string
// RemoveAllEdgesFrom removes all edges incident from n in O(degree) time
// and returns the outgoing neighbour keys that were removed. For undirected
// graphs the mirror entries are also removed. The per-pair edge state
// (labels, properties, handle metadata, CREATE counters) is cleared for
// every removed pair, exactly as a sequence of RemoveEdge calls would do.
//
// Callers that also need to roll back undo-log entries or emit WAL records
// must capture the neighbours via OutNeighbours before calling this method;
// RemoveAllEdgesFrom does not emit WAL records on its own.
RemoveAllEdgesFrom(n string)
// OutDegree returns the number of outgoing edges from n.
OutDegree(n string) int
// ResolveNodeID translates a user-facing node key to its internal NodeID,
// returning ok=false when the node has not been interned yet.
ResolveNodeID(n string) (graph.NodeID, bool)
// ResolveNodeLabel translates an internal NodeID back to the user-facing
// node key, returning ok=false when id is unknown.
ResolveNodeLabel(id graph.NodeID) (string, bool)
// WalkNodeIDs calls fn for every node currently interned in the graph.
WalkNodeIDs(fn func(graph.NodeID) bool)
}
GraphMutator is the write surface exposed to Cypher write operators.
All methods accept the user-facing node key (string) used by the lpg.Graph[string,float64] instantiation. The graph is responsible for interning the value and returning the stable internal NodeID where applicable.
GraphMutator is NOT safe for concurrent use from multiple goroutines; each physical operator tree owns exactly one instance.
type HashJoin ¶ added in v0.3.1
type HashJoin struct {
// contains filtered or unexported fields
}
HashJoin is a Volcano pipeline operator that performs an order-insensitive equi-join between two independent (uncorrelated) plans. It replaces a nested-loop Apply + equi-join Filter when the planner has proven the substitution is result-identical and order-safe.
HashJoin is NOT safe for concurrent use.
func NewHashJoin ¶ added in v0.3.1
NewHashJoin creates a HashJoin.
- build is the plan whose rows are fully materialised into the hash table.
- probe is the plan streamed against the table.
- buildFn / probeFn extract the join key from a build / probe row.
- buildOnLeft selects the output column order (see HashJoin.buildOnLeft).
HashJoin takes ownership of both plans; callers must not use them afterwards.
func (*HashJoin) PlanChildren ¶ added in v0.11.0
PlanChildren reports the build side first, then the probe side: the build input is materialised into the hash table before a single probe row is read, which is the order that explains the join's cost.
func (*HashJoin) PlanDetail ¶ added in v0.11.0
PlanDetail reports which side of the join was materialised into the hash table. The build side is the operator's cost centre and the reason one input order beats the other, and it is not visible from the operator name.
func (*HashJoin) WithByteBudget ¶ added in v0.7.0
WithByteBudget bounds the estimated retained size of the build table by maxBytes, returning ErrHashJoinMemoryExceeded when exceeded. The build table has no count cap, so this is the operator's memory bound (#1841). A non-positive maxBytes or nil estimateRow leaves it unbounded (prior behaviour). Returns op for chaining and must be called before Init.
type HashLookup ¶ added in v0.11.0
type HashLookup interface {
// LookupAppend appends the NodeIDs matching the seek value to dst in
// ascending order and returns the extended slice, draining the index's
// posting list under its read lock without materialising a bitmap.
// Returns ErrIndexTypeMismatch when the value kind is incompatible.
LookupAppend(value expr.Value, dst []uint64) ([]uint64, error)
}
HashLookup is the minimal interface that NodeByIndexSeek requires. hash.Index[V] satisfies it for every supported V.
Exported so the engine's build path can name it when it chooses between the guarded and unguarded seek forms in one place (rmp #2423).
type IndexBuffer ¶
type IndexBuffer struct {
// contains filtered or unexported fields
}
IndexBuffer collects index.Change events produced by write operators during a single write transaction.
Call Enqueue for every graph mutation. At the transaction boundary:
- Commit: fans changes to index.Manager.ApplyBatch then resets.
- Rollback: discards changes without touching indexes.
IndexBuffer is NOT safe for concurrent use.
func (*IndexBuffer) Commit ¶
func (b *IndexBuffer) Commit(mgr *index.Manager)
Commit applies all buffered changes to mgr via ApplyBatch, then resets the buffer. A nil mgr is safe: changes are discarded without panicking.
func (*IndexBuffer) Enqueue ¶
func (b *IndexBuffer) Enqueue(c index.Change)
Enqueue appends c to the buffer.
func (*IndexBuffer) Len ¶
func (b *IndexBuffer) Len() int
Len returns the number of changes currently buffered.
func (*IndexBuffer) Rollback ¶
func (b *IndexBuffer) Rollback()
Rollback discards all buffered changes without applying them.
type IndexKindExec ¶
type IndexKindExec uint8
IndexKindExec distinguishes hash vs. btree in the exec layer.
const ( // ExecIndexHash creates a hash.Index[string]. ExecIndexHash IndexKindExec = iota // ExecIndexBTree creates a btree.Index[string]. ExecIndexBTree )
type IndexNestedLoopJoin ¶ added in v0.11.0
type IndexNestedLoopJoin struct {
// contains filtered or unexported fields
}
IndexNestedLoopJoin joins an outer arm against an indexed population by seeking the index once per outer row. It emits outer||inner rows in the nested loop's exact sequence.
IndexNestedLoopJoin is NOT safe for concurrent use.
func NewIndexNestedLoopJoin ¶ added in v0.11.0
func NewIndexNestedLoopJoin(outer, inner Operator, idx NumericPointLookup, outerKeyFn, innerKeyFn KeyFn) *IndexNestedLoopJoin
NewIndexNestedLoopJoin creates an IndexNestedLoopJoin.
- outer is the probe arm; its rows drive the join and set the output's major order.
- inner is the plain inner arm, used only for the non-numeric-key fallback.
- idx is the numeric btree companion covering the inner arm's (label, property).
- outerKeyFn / innerKeyFn evaluate the join key against an outer row and an inner-only row respectively.
func (*IndexNestedLoopJoin) Close ¶ added in v0.11.0
func (op *IndexNestedLoopJoin) Close() error
Close closes both arms. The inner arm is closed only if it was ever Init'd.
func (*IndexNestedLoopJoin) Init ¶ added in v0.11.0
func (op *IndexNestedLoopJoin) Init(ctx context.Context) error
Init initialises the outer arm. The inner arm is Init'd lazily, on the first row that needs the fallback: a query whose keys are all numeric never touches it, so it never pays the scan's set-up.
func (*IndexNestedLoopJoin) Next ¶ added in v0.11.0
func (op *IndexNestedLoopJoin) Next(out *Row) (bool, error)
Next emits the next outer||inner row.
func (*IndexNestedLoopJoin) PlanChildren ¶ added in v0.11.0
func (op *IndexNestedLoopJoin) PlanChildren() []Operator
PlanChildren reports the outer input that drives the seek, then the inner arm.
The order is the OPPOSITE of the hash joins' above, and for the same reason theirs is build-first: it is the order that explains the cost. This join materialises nothing — the outer arm drives it and each outer row is answered by an index seek, so the outer side is what the row count multiplies.
The inner arm is reported even though most queries never execute it: it serves only the fallback path (an integer key too large for float64 to represent exactly), and rendering it is what makes that path visible in a plan rather than a surprise in a profile.
func (*IndexNestedLoopJoin) WithProvenNumericCoverage ¶ added in v0.11.0
func (op *IndexNestedLoopJoin) WithProvenNumericCoverage(proven bool) *IndexNestedLoopJoin
WithProvenNumericCoverage records that every node the inner scan produces is known to carry a numeric value for the join property. The operator then answers a non-numeric key with no rows instead of scanning for them. It returns op for chaining.
Only a caller that has actually PROVED this may set it — the planner does, by comparing the numeric index's entry count against the scan's row count. Setting it without the proof would drop rows.
type IndexRangePart ¶ added in v0.11.0
type IndexRangePart struct {
Index rangeLookup
Lo RangeBound
Hi RangeBound
}
IndexRangePart is one indexed conjunct's contribution to a composed intersection: the index to probe and the bounds to probe it over (#2134).
type Int64HashIndex ¶
type Int64HashIndex struct {
// contains filtered or unexported fields
}
Int64HashIndex adapts hash.Index[int64] to the HashLookup interface. It accepts only expr.IntegerValue seek keys.
func NewInt64HashIndex ¶
func NewInt64HashIndex(idx interface {
LookupAppend(value int64, dst []uint64) []uint64
}) *Int64HashIndex
NewInt64HashIndex constructs an Int64HashIndex.
func (*Int64HashIndex) LookupAppend ¶ added in v0.6.0
LookupAppend implements HashLookup.
type Int64RangeIndex ¶
type Int64RangeIndex struct {
// contains filtered or unexported fields
}
Int64RangeIndex adapts btree.Index[int64] to the [rangeLookup] interface. Nil bounds are treated as ±∞ using math.MinInt64 / math.MaxInt64.
func NewInt64RangeIndex ¶
func NewInt64RangeIndex(idx interface {
Range(lo, hi int64) *roaring64.Bitmap
}) *Int64RangeIndex
NewInt64RangeIndex constructs an Int64RangeIndex.
func (*Int64RangeIndex) RangeBitmap ¶
func (r *Int64RangeIndex) RangeBitmap(lo, hi expr.Value) *roaring64.Bitmap
RangeBitmap implements [rangeLookup].
type IntersectAdjacencySource ¶ added in v0.11.0
type IntersectAdjacencySource func() (fwd, rev CSRAdjacency, midFilter, endFilter map[uint64]string)
IntersectAdjacencySource is AdjacencySource for the fused cyclic expand, which filters TWO legs and therefore needs two type filters keyed to the one adjacency it resolves.
func StaticIntersectAdjacency ¶ added in v0.11.0
func StaticIntersectAdjacency(fwd, rev CSRAdjacency, midFilter, endFilter map[uint64]string) IntersectAdjacencySource
StaticIntersectAdjacency is StaticAdjacency for an IntersectAdjacencySource, and carries the same warning: a production plan must not use it.
type KeyFn ¶ added in v0.3.1
KeyFn extracts the join-key value from a row. It returns the evaluated key (which may be expr.Null) or an error that halts the pipeline.
type LPGLabelSource ¶
type LPGLabelSource struct {
// contains filtered or unexported fields
}
LPGLabelSource is a concrete [labelResolver] built from an lpg label registry and a label index. It is the standard adapter for tests and production use.
func NewLPGLabelSource ¶
NewLPGLabelSource constructs a LPGLabelSource. lookupFn should wrap lpg.LabelRegistry.Lookup, casting LabelID to uint32.
func (*LPGLabelSource) ResolveLabelBitmap ¶
func (s *LPGLabelSource) ResolveLabelBitmap(name string) *roaring64.Bitmap
ResolveLabelBitmap implements [labelResolver].
type LabelCountScan ¶ added in v0.9.0
type LabelCountScan struct {
// contains filtered or unexported fields
}
LabelCountScan is a Volcano leaf operator that computes a group-by-less count over a bare single-label node scan by reading the label's live-node count directly. It emits exactly one row with a single expr.IntegerValue column carrying that count.
LabelCountScan is NOT safe for concurrent use.
func NewLabelCountScan ¶ added in v0.9.0
func NewLabelCountScan(label string, src labelResolver) *LabelCountScan
NewLabelCountScan creates a LabelCountScan that counts the nodes carrying label via src.
func (*LabelCountScan) Close ¶ added in v0.9.0
func (op *LabelCountScan) Close() error
Close releases resources held by the operator. LabelCountScan holds none, so Close is a no-op and is safe to call whether or not Next was ever called.
func (*LabelCountScan) Init ¶ added in v0.9.0
func (op *LabelCountScan) Init(ctx context.Context) error
Init reads the label's live-node count once. It prefers the zero-alloc direct count when src supports it and otherwise falls back to the cardinality of the resolved bitmap — both yield the same value.
type LabelIntersectResolver ¶ added in v0.11.0
LabelIntersectResolver resolves a CONJUNCTION of label names to the bitmap of NodeIDs carrying EVERY one of them — the set-at-a-time answer to a multi-label node pattern (#2133).
The names arrive in the order the caller wants them intersected, which is load-bearing rather than cosmetic: label.Index.Intersect clones the FIRST label's bitmap, so passing the smallest label first is measured 6.0× faster and 7.5× lighter than passing the largest (docs/design-bitmap-intersection.md §4). The planner sorts by exact ascending cardinality; this interface preserves that order rather than re-deriving it.
type Limit ¶
type Limit struct {
// contains filtered or unexported fields
}
Limit is a Volcano pipeline operator that forwards at most n rows from its child operator and then signals end-of-stream.
Limit is NOT safe for concurrent use.
func NewLimit ¶
NewLimit creates a Limit operator that passes at most n rows from child. n must be ≥ 0; a limit of 0 emits no rows.
func (*Limit) Next ¶
Next forwards the next row from the child, unless the limit has been reached, in which case it returns (false, nil) immediately.
func (*Limit) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input it truncates.
type MapEvalFn ¶ added in v0.9.0
MapEvalFn is a per-row evaluator for a whole property map used by a `SET x = {…}` / `SET x += {…}` operator. Unlike PropsEvalFn it also reports the keys whose value evaluated to null (nullKeys): SET-map semantics REMOVE such keys from the target (openCypher: `SET n += {k: null}` deletes k), whereas PropsEvalFn omits null entries because a null on CREATE is a no-op. entries carries the non-null (key, value) pairs; nullKeys the keys to delete.
type Merge ¶
type Merge struct {
// contains filtered or unexported fields
}
Merge implements MERGE semantics: match-or-create a pattern.
Merge is NOT safe for concurrent use.
func NewMerge ¶
func NewMerge( nodeVar string, labels []string, properties string, onCreateStrs, onMatchStrs []string, searchFn MergeSearchFn, schema map[string]int, child Operator, mutator GraphMutator, ) (*Merge, error)
NewMerge creates a Merge operator.
nodeVar is the variable bound to the merged node. labels and properties are the node-pattern components used when creating a new node. onCreateStrs and onMatchStrs are opaque SET-item strings from the IR translator. searchFn executes the read side of the match. schema maps variable names to column indices. mutator is the graph write surface.
func (*Merge) Init ¶
Init initialises the operator: executes the search plan, then dispatches to the ON MATCH or ON CREATE branch depending on whether the search returned any rows.
The first Merge.Init (or CreateNode.Init) in the process also seeds [globalNodeCounter] past the largest synthetic key already interned in op.mutator, so that the keys minted by [Merge.freshNodeKey] in this process cannot collide with __cx_merge_<hex> keys persisted by an earlier process and replayed during WAL / snapshot recovery. Without this a one-process-per-command consumer mints __cx_merge_1 on every command and the second MERGE silently overwrites the first node. The seed is gated by [globalNodeCounterSeededOnce] so the O(N) scan runs at most once per process regardless of how many CreateNode / Merge operators are built.
func (*Merge) Next ¶
Next emits one row: either a matched row (ON MATCH) or the created row (ON CREATE), each emitted exactly once.
func (*Merge) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input whose rows drive the merge.
func (*Merge) WithActionEvals ¶ added in v0.8.0
func (op *Merge) WithActionEvals(onCreate, onMatch map[string]ValueEvalFn) *Merge
WithActionEvals attaches per-row RHS evaluators for ON CREATE / ON MATCH property-set items whose right-hand side is a non-literal expression (keyed by MergeActionEvalKey). Without these, a self-referential assignment such as `ON MATCH SET n.num = n.num + 1` fails to parse as a literal and is silently dropped (#1965). Returns op for chaining.
func (*Merge) WithConstraints ¶
func (op *Merge) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *Merge
WithConstraints attaches a ConstraintRegistry and index.Manager for pre-write enforcement in ON CREATE and ON MATCH actions. Both must be non-nil. Returns op for chaining.
func (*Merge) WithLabelSource ¶ added in v0.11.0
func (op *Merge) WithLabelSource(src MergeLabelSource) *Merge
WithLabelSource attaches the label posting-list source that narrows the row-aware merge search to the nodes carrying a pattern label, instead of every interned node (#2217). It is the access path that makes the UNWIND-MERGE bulk-ingest idiom scale with the label's population rather than with the size of the whole graph.
src may be nil, in which case the search keeps the full walk. Returns op for chaining.
func (*Merge) WithParams ¶
WithParams re-parses the property map with the supplied query parameters for $name substitution. Returns op for chaining.
func (*Merge) WithPropsEvalFn ¶
func (op *Merge) WithPropsEvalFn(fn PropsEvalFn) *Merge
WithPropsEvalFn attaches a per-row property evaluator. When fn is non-nil the operator re-evaluates the MERGE node-pattern property map against each driving row and uses the merged (literal ∪ dynamic) property set both as the search predicate and as the ON CREATE node-property writes. Required for MERGE patterns whose inline property map contains variable references such as `MERGE (p:Person {login: prop.login})` after an UNWIND.
Returns op for chaining.
func (*Merge) WithSetAllActions ¶ added in v0.9.0
func (op *Merge) WithSetAllActions(onCreate, onMatch []MergeSetAllAction) *Merge
WithSetAllActions attaches whole-entity ON CREATE / ON MATCH SET actions (`SET n = <expr>` / `SET n += <expr>`), which the per-property action path cannot represent. Each is evaluated per row and applied via [applyWholeEntityValueToNode] (#2031). Returns op for chaining.
type MergeLabelSource ¶ added in v0.11.0
type MergeLabelSource interface {
// ResolveLabelBitmap returns the NodeIDs carrying name, or an empty bitmap
// when the label is unknown. An empty bitmap is authoritative: no node can
// carry a label that was never interned, so the MERGE correctly finds no
// match and fires ON CREATE.
ResolveLabelBitmap(name string) *roaring64.Bitmap
}
MergeLabelSource narrows the MERGE match phase to the nodes that carry a pattern label. lpg's label index satisfies it, and its bitmap reflects uncommitted writes made by the enclosing transaction — verified for a same-transaction CREATE and for a label added by SET — so driving from it cannot miss a node the caller has just written and thereby create a duplicate.
type MergePattern ¶ added in v0.7.0
type MergePattern struct {
// contains filtered or unexported fields
}
MergePattern matches-or-creates a chain of one or more relationship hops in which at least one node is not already bound. See the package doc above for the full algorithm.
func NewMergePattern ¶ added in v0.7.0
func NewMergePattern(child Operator, mutator GraphMutator) *MergePattern
NewMergePattern creates an empty MergePattern; call AddBoundNode/ AddFreshNode once per chain position (in order) and AddHop once per relationship (len(hops) == positions added - 1), then WithActions.
func (*MergePattern) AddBoundNode ¶ added in v0.7.0
func (op *MergePattern) AddBoundNode(varName string, col int) *MergePattern
AddBoundNode appends a chain position whose value is already bound by an earlier clause, read from input-row column col.
func (*MergePattern) AddFreshNode ¶ added in v0.7.0
func (op *MergePattern) AddFreshNode(varName string, labels []string, propsRaw string, outCol int) *MergePattern
AddFreshNode appends a chain position this MERGE clause introduces fresh. outCol is the output-row column that receives the matched-or-created NodeID as an expr.IntegerValue; pass -1 if the variable is never referenced downstream (still searched/created, just not projected).
func (*MergePattern) AddHop ¶ added in v0.7.0
func (op *MergePattern) AddHop(relVar string, relCol int, relType, relPropsRaw string, undirected, reversed bool) *MergePattern
AddHop appends the relationship connecting the two most-recently-added chain positions. relCol is the output-row column for a named relationship variable, or -1 for an anonymous one. reversed is true for an incoming `<-` pattern, where the edge is stored from the next chain position back to the current one.
func (*MergePattern) Close ¶ added in v0.7.0
func (op *MergePattern) Close() error
Close closes the child operator.
func (*MergePattern) Init ¶ added in v0.7.0
func (op *MergePattern) Init(ctx context.Context) error
Init initialises the operator and its child. The first MergePattern.Init (or CreateNode.Init / Merge.Init) in the process also seeds [globalNodeCounter], exactly as those operators do, so fresh-node keys minted here cannot collide with __cx_merge_<hex> keys replayed from an earlier process during WAL / snapshot recovery.
func (*MergePattern) MarkHopRefsPatternNode ¶ added in v0.9.0
func (op *MergePattern) MarkHopRefsPatternNode() *MergePattern
MarkHopRefsPatternNode flags the most-recently-added hop as having an inline relationship property map that references an earlier same-pattern node, so its properties are evaluated per binding rather than via the once-per-row precomputation (#2024). Returns op for chaining.
func (*MergePattern) Next ¶ added in v0.7.0
func (op *MergePattern) Next(out *Row) (bool, error)
Next drives one search-or-create cycle per input row, buffering multiple matches (Merge5-style multiplicity, generalised: a joint pattern can have more than one satisfying binding) so each is emitted as its own row. Mirrors Merge.Next's buffering shape, including firing exactly once against an empty driving row when MergePattern is the leading clause (no preceding MATCH/WITH at all).
func (*MergePattern) PlanChildren ¶ added in v0.11.0
func (op *MergePattern) PlanChildren() []Operator
PlanChildren reports the input whose rows drive the pattern merge.
func (*MergePattern) WithActionEvals ¶ added in v0.8.0
func (op *MergePattern) WithActionEvals(onCreate, onMatch map[string]ValueEvalFn) *MergePattern
WithActionEvals attaches per-row RHS evaluators for ON CREATE / ON MATCH property-set items whose right-hand side is a non-literal expression (keyed by MergeActionEvalKey on the target variable and property key). Without these, an expression such as `ON MATCH SET n.num = n.num + 1` fails to parse as a literal and is dropped (#1965). Returns op for chaining.
func (*MergePattern) WithActions ¶ added in v0.7.0
func (op *MergePattern) WithActions(onCreateStrs, onMatchStrs []string) (*MergePattern, error)
WithActions parses and attaches ON CREATE / ON MATCH SET items (the same opaque-string representation Merge uses). Each item may target any variable in the chain (a fresh node, a bound node, or a hop's relationship variable) — dispatched at apply time by the parsed action's target name.
func (*MergePattern) WithConstraints ¶ added in v0.7.0
func (op *MergePattern) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *MergePattern
WithConstraints attaches a ConstraintRegistry and index.Manager for pre-write enforcement of every fresh node's created properties and of every ON CREATE / ON MATCH node property action — mirroring Merge.WithConstraints and CreateNode.WithConstraints. Both must be non-nil. Returns op for chaining.
func (*MergePattern) WithHopPropsEvalFn ¶ added in v0.9.0
func (op *MergePattern) WithHopPropsEvalFn(fn PropsEvalFn) *MergePattern
WithHopPropsEvalFn attaches a per-row property evaluator to the most-recently-added hop (mirroring MergePattern.WithNodePropsEvalFn). Used when that hop's relationship property map contains a non-literal expression (a variable reference, property access, or arithmetic — e.g. `(a)-[:R {kind: row.pk}]->(b)`) that [parsePropLiteral] cannot resolve at plan-construction time. The dynamic entries are merged with the map's literal entries at both search and create time (see [MergePattern.effectiveHopProps]), taking precedence on key collision. Without this, a hop's row-driven relationship property would be silently dropped from both the search predicate and the created edge — the same defect class this operator exists to eliminate, previously rejected at build time on the compound-pattern path.
func (*MergePattern) WithLabelSource ¶ added in v0.11.0
func (op *MergePattern) WithLabelSource(src MergeLabelSource) *MergePattern
WithLabelSource attaches the label posting-list source that narrows the anchor-node search to the nodes carrying a pattern label, instead of every interned node (#2217). src may be nil, which keeps the full walk. Returns op for chaining.
func (*MergePattern) WithNodePropsEvalFn ¶ added in v0.7.0
func (op *MergePattern) WithNodePropsEvalFn(fn PropsEvalFn) *MergePattern
WithNodePropsEvalFn attaches a per-row property evaluator to the most-recently-added fresh node (mirroring [CreateNode.WithPropsEvalFn| CreateNode.WithPropsEvalFn]). Used when that node's property map contains non-literal expressions (parameters, variable references) that [parsePropLiteral] cannot resolve at plan-construction time; the dynamic entries are merged with the map's literal entries at both search and create time (see [mergeProps]), taking precedence on key collision. Without this, a node position's parameterised properties would be silently dropped from both the search predicate and the created node — the same defect class this operator exists to eliminate.
func (*MergePattern) WithParams ¶ added in v0.7.0
func (op *MergePattern) WithParams(params map[string]expr.Value) (*MergePattern, error)
WithParams attaches query parameters for $name substitution in every node's and every hop's inline property map, re-parsing each non-empty raw map with parameter references resolved to concrete literal values. Mirrors CreateNode.WithParams: resolving parameters once here, at build time, is cheaper than a per-row PropsEvalFn and is correct because parameter values are constant for the whole query execution. Must be called after every AddBoundNode/AddFreshNode/AddHop call it should cover.
func (*MergePattern) WithSetAllActions ¶ added in v0.9.0
func (op *MergePattern) WithSetAllActions(onCreate, onMatch []MergeSetAllAction) *MergePattern
WithSetAllActions attaches whole-entity ON CREATE / ON MATCH SET actions (`SET n = <expr>` / `SET n += <expr>`) on a chain node variable, which the per-property action path cannot represent. Each is evaluated per row and applied via [applyWholeEntityValueToNode] (#2031). Returns op for chaining.
type MergeRelAction ¶
type MergeRelAction struct {
// contains filtered or unexported fields
}
MergeRelAction is a pre-parsed `SET <relVar>.<key> = <value>` item, or a whole-entity REPLACE sentinel (#1687).
Three shapes:
- Single-property write: key != "", value is the literal string.
- Entity-copy: key == "", value == "<sourceVar>". When replace is true the edge's properties absent from the source entity are cleared first.
- Replace-map sentinel: key == "", value == "", replace == true. retainKeys lists the RHS map keys; the edge's properties absent from retainKeys are cleared. The sentinel is immediately followed by the per-key write actions for the map, so the clear precedes the writes. An empty (non-nil) retainKeys clears every property (`SET r = {}`).
func MergeRelActionFromKV ¶
func MergeRelActionFromKV(key, value string) MergeRelAction
MergeRelActionFromKV constructs a MergeRelationship ON CREATE / ON MATCH action from a (key, value) pair. value is the opaque literal string as it appears in the source query (e.g. `'foo'` or `42`).
func MergeRelActionReplaceFromKV ¶ added in v0.6.0
func MergeRelActionReplaceFromKV(key, value string, replace bool, retainKeys []string) MergeRelAction
MergeRelActionReplaceFromKV constructs a MergeRelationship ON CREATE / ON MATCH action carrying the whole-entity REPLACE marker (#1687). When replace is true the action is either a replace-map sentinel (key == "", value == "", retainKeys lists the RHS keys to keep) or an entity-copy replace (key == "", value == "<sourceVar>", retainKeys nil → retain the source's live keys). retainKeys is copied defensively so the caller may reuse its slice. When replace is false this is equivalent to MergeRelActionFromKV.
type MergeRelationship ¶
type MergeRelationship struct {
// contains filtered or unexported fields
}
MergeRelationship matches-or-creates a single-hop directed relationship between two already-bound endpoint columns. ON CREATE / ON MATCH actions targeting the relationship variable are applied to the matched-or-created edge.
MergeRelationship is NOT safe for concurrent use.
func NewMergeRelationship ¶
func NewMergeRelationship(child Operator, srcCol, dstCol int, relType string, mutator GraphMutator) *MergeRelationship
NewMergeRelationship constructs a MergeRelationship operator.
- child is the upstream plan providing rows with the bound endpoints.
- srcCol / dstCol are the column indices that hold the src / dst NodeID.
- relType is the relationship type label (single label only).
- mutator is the graph write surface.
func (*MergeRelationship) Close ¶
func (op *MergeRelationship) Close() error
Close closes the child operator.
func (*MergeRelationship) Init ¶
func (op *MergeRelationship) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*MergeRelationship) Next ¶
func (op *MergeRelationship) Next(out *Row) (bool, error)
Next emits the next input row, ensuring that the (src)-[:relType]->(dst) edge exists in the graph (either pre-existing or newly created). When an existing edge has CREATE-multiplicity N > 1 the operator emits N rows for the same upstream tuple (Merge5 [3]).
func (*MergeRelationship) PlanChildren ¶ added in v0.11.0
func (op *MergeRelationship) PlanChildren() []Operator
PlanChildren reports the input whose rows drive the relationship merge.
func (*MergeRelationship) WithActionEvals ¶ added in v0.8.0
func (op *MergeRelationship) WithActionEvals(onCreate, onMatch map[string]ValueEvalFn) *MergeRelationship
WithActionEvals attaches per-row RHS evaluators for ON CREATE / ON MATCH property-set items whose right-hand side is a non-literal expression (keyed by MergeActionEvalKey on the relationship variable and property key). Without these, `ON MATCH SET r.n = r.n + 1` fails to parse as a literal and, on this fast path, surfaced a parse error instead of incrementing the edge property (#1965). Returns op for chaining.
func (*MergeRelationship) WithOnCreate ¶
func (op *MergeRelationship) WithOnCreate(relVar string, actions []MergeRelAction) *MergeRelationship
WithOnCreate registers ON CREATE SET actions to apply when the edge is newly created. Each action is `<relVar>.<key> = <value>`; the caller has already verified that every action targets the relationship variable bound by this operator.
func (*MergeRelationship) WithOnMatch ¶
func (op *MergeRelationship) WithOnMatch(relVar string, actions []MergeRelAction) *MergeRelationship
WithOnMatch registers ON MATCH SET actions to apply when the edge already exists.
func (*MergeRelationship) WithParams ¶ added in v0.9.0
func (op *MergeRelationship) WithParams(params map[string]expr.Value) (*MergeRelationship, error)
WithParams attaches query parameters for $name substitution in the inline relationship property map, re-parsing the raw map with parameter references resolved to concrete literal values. Mirrors CreateNode.WithParams: resolving parameters once here, at build time, is cheaper than a per-row evaluator and is correct because parameter values are constant for the whole query execution. Without it a parameterised inline property such as `MERGE (a)-[r:T {kind: $pk}]->(b)` is silently dropped, since the literal-only parser skips $param references (they are deferred to a resolver). Returns op for chaining.
func (*MergeRelationship) WithRelColumn ¶
func (op *MergeRelationship) WithRelColumn(relCol int) *MergeRelationship
WithRelColumn registers the output-row column index that will carry the matched / created edge ID. When set (relCol >= 0) MergeRelationship extends the row with an IntegerValue(edgeID) at the column so downstream operators (RETURN r, count(r), …) see the bound relationship.
func (*MergeRelationship) WithRelProperties ¶
func (op *MergeRelationship) WithRelProperties(propsRaw string) *MergeRelationship
WithRelProperties registers an inline relationship property predicate (e.g. `{name: 'r2'}` from `MERGE (a)-[r:T {name: 'r2'}]->(b)`). When set, the operator filters the existing-edge search by the predicate AND writes the listed properties when a new edge is created. Pass an empty string to clear.
func (*MergeRelationship) WithRelPropsEvalFn ¶ added in v0.9.0
func (op *MergeRelationship) WithRelPropsEvalFn(fn PropsEvalFn) *MergeRelationship
WithRelPropsEvalFn attaches a per-row evaluator for the inline relationship property map when it contains a non-literal value (a variable reference, property access, or arithmetic expression — e.g. `MERGE (a)-[r:T {kind: row.pk}]->(b)`). The merged (literal ∪ dynamic) property set drives both the existing-edge search predicate and the created edge's properties, exactly as the node Merge path does via [mergeProps]. Without it the literal-only parser drops the non-literal entry, so the property is neither searched on nor written — the created edge stores null (fail-silent Consistency defect). Pass nil to clear. Returns op for chaining.
func (*MergeRelationship) WithSchema ¶
func (op *MergeRelationship) WithSchema(schema map[string]int) *MergeRelationship
WithSchema attaches the upstream variable-to-column mapping so entity-copy actions (`SET r = a`) can resolve the source variable from the row at write time.
func (*MergeRelationship) WithUndirected ¶
func (op *MergeRelationship) WithUndirected(u bool) *MergeRelationship
WithUndirected toggles the undirected-search behaviour. When true, the match phase probes both (src, dst) and (dst, src) directions before falling through to the edge-create path, matching the openCypher semantics of `MERGE (a)-[r:T]-(b)` (Merge5 [13]).
type MergeSearchFn ¶
MergeSearchFn is a function that executes the search sub-plan for the MERGE pattern and returns the matching rows. An empty slice means no match.
func NewMergeSearchFnFromPattern ¶
func NewMergeSearchFnFromPattern( labels []string, propertiesRaw string, params map[string]expr.Value, mutator GraphMutator, labelSrc MergeLabelSource, ) (MergeSearchFn, error)
NewMergeSearchFnFromPattern returns a MergeSearchFn that finds every node in mutator whose label set contains every label in labels and whose property bag is equal to every (key, value) parsed from propertiesRaw.
labels is the slice of pattern labels (may be empty when the pattern is e.g. `(n {key: v})`). propertiesRaw is the opaque literal-map string surfaced by the IR (e.g. `{name: "Alice", age: 30}`); it may be empty. params binds `$name` references in propertiesRaw to query parameters; when empty the parser ignores parameter substitution.
The function returned by NewMergeSearchFnFromPattern enumerates candidate nodes, resolves the label and property bag, and admits the node iff every label and every property matches. When the pattern carries at least one label and labelSrc is non-nil the candidates come from the smallest matching label's posting list, so cost tracks that label's population rather than the whole graph; otherwise every interned node is examined. See [walkMergeCandidates].
labelSrc may be nil, in which case the search falls back to the full walk.
type MergeSetAllAction ¶ added in v0.9.0
type MergeSetAllAction struct {
Eval ExprValueEvalFn
TargetVar string
IsReplace bool
}
MergeSetAllAction is a whole-entity ON CREATE / ON MATCH SET action (`SET n = <expr>` / `SET n += <expr>`) evaluated per row. TargetVar names the node variable the action writes to; IsReplace selects `=` (true, replace all) vs `+=` (false, merge) semantics; Eval evaluates the right-hand side against the merged/created row.
type NodeByIndexRangeScan ¶
type NodeByIndexRangeScan struct {
// contains filtered or unexported fields
}
NodeByIndexRangeScan is a Volcano leaf operator that scans a B+tree index over a half-open, closed, or open interval. Each Row has a single column: expr.IntegerValue(nodeID).
NodeByIndexRangeScan is NOT safe for concurrent use.
func NewNodeByIndexIntersectionScan ¶ added in v0.11.0
func NewNodeByIndexIntersectionScan(idx rangeLookup, lo, hi RangeBound, parts []IndexRangePart) *NodeByIndexRangeScan
NewNodeByIndexIntersectionScan composes SEVERAL single-property indexes into one access path by intersecting their range bitmaps (#2134).
This is what lets `WHERE n.a > 1 AND n.b < 9` be answered from two ordinary single-property indexes — the answer Memgraph needs a dedicated COMPOSITE index type for. No new index type and no new statistic are involved: RangeBitmap already returns a Roaring bitmap, so the conjunction is a set operation.
Superset discipline (design §8) ¶
Unlike the label intersection, each part is only a SUPERSET of its conjunct's true matches — the operator emits the inclusive [lo, hi] interval and cannot enforce an open bound (see the type doc, #F-EXEC1). Intersection PRESERVES that: if Bᵃ ⊇ Aᵃ and Bᵇ ⊇ Aᵇ then Bᵃ ∩ Bᵇ ⊇ Aᵃ ∩ Aᵇ. So the composed scan is a sound superset — and the caller's residual Filter remains MANDATORY, exactly as it is for a single range scan.
parts must hold at least one entry beyond the primary; the primary's own bounds are given by lo/hi. Parts are ANDed in the order supplied, which the planner orders by ascending exact cardinality so the cheapest bitmap is materialised first.
func NewNodeByIndexRangeScan ¶
func NewNodeByIndexRangeScan(idx rangeLookup, lo, hi RangeBound) *NodeByIndexRangeScan
NewNodeByIndexRangeScan creates a NodeByIndexRangeScan.
It carries NO label restriction; a rewrite that replaced a labelled scan leaf must use NodeByIndexRangeScan.RestrictToLabel. See the labelBM field.
func (*NodeByIndexRangeScan) Close ¶
func (op *NodeByIndexRangeScan) Close() error
Close releases resources.
func (*NodeByIndexRangeScan) Init ¶
func (op *NodeByIndexRangeScan) Init(ctx context.Context) error
Init performs the range lookup — intersecting the additional indexed conjuncts when this is a composed scan (#2134) — and initialises the bitmap iterator.
func (*NodeByIndexRangeScan) Next ¶
func (op *NodeByIndexRangeScan) Next(out *Row) (bool, error)
Next emits the next NodeID in the inclusive [lo, hi] superset. Returns (false, nil) at end-of-stream. Exact open/closed enforcement is the caller's residual-filter responsibility (see the type doc); this operator emits every NodeID the index's range bitmap contains.
func (*NodeByIndexRangeScan) PlanDetail ¶ added in v0.11.0
func (op *NodeByIndexRangeScan) PlanDetail() string
PlanDetail reports the bounds of the range this scan walks, so a reader can see whether the seek is a point lookup, a half-open range, or effectively a full index walk.
func (*NodeByIndexRangeScan) RestrictToLabel ¶ added in v0.11.0
func (op *NodeByIndexRangeScan) RestrictToLabel(resolve func() *roaring64.Bitmap) *NodeByIndexRangeScan
RestrictToLabel makes this scan intersect its candidates with the label bitmap resolve returns, and returns op so a builder can chain it onto a constructor.
resolve is called once per Init, so it observes the reading transaction's own snapshot rather than a bitmap captured at plan time. A nil resolve is a no-op, which keeps the unlabelled shape expressible without a second constructor.
type NodeByIndexSeek ¶
type NodeByIndexSeek struct {
// contains filtered or unexported fields
}
NodeByIndexSeek is a Volcano leaf operator that performs an equality lookup on a property hash index. Each Row has a single column: expr.IntegerValue(nodeID).
NodeByIndexSeek is NOT safe for concurrent use.
func NewNodeByIndexSeek ¶
func NewNodeByIndexSeek(idx HashLookup, seekValue expr.Value) *NodeByIndexSeek
NewNodeByIndexSeek creates a NodeByIndexSeek that looks up seekValue in idx.
It carries NO residual predicate, so it is correct only where the index's candidates need no further qualification. A rewrite that dropped a label must use NewNodeByIndexSeekAdmitting; see [NodeByIndexSeek.admit].
func NewNodeByIndexSeekAdmitting ¶ added in v0.11.0
func NewNodeByIndexSeekAdmitting(idx HashLookup, seekValue expr.Value, admit func(nodeID uint64) bool) *NodeByIndexSeek
NewNodeByIndexSeekAdmitting is NewNodeByIndexSeek with a residual predicate applied to every candidate the index returns — the label check a rewrite over a labelled scan leaf owes (rmp #2423).
admit must be non-nil; a caller with nothing to verify uses NewNodeByIndexSeek so the distinction stays visible at the call site rather than hiding behind a nil.
func (*NodeByIndexSeek) Init ¶
func (op *NodeByIndexSeek) Init(ctx context.Context) error
Init performs the index lookup, draining the matching NodeIDs into the operator's reused buffer. The dominant singleton/small posting list fits the inline idbuf, so a seek allocates nothing after the buffer is established.
func (*NodeByIndexSeek) Next ¶
func (op *NodeByIndexSeek) Next(out *Row) (bool, error)
Next emits the next matching NodeID. Returns (false, nil) at end-of-stream.
func (*NodeByIndexSeek) PlanDetail ¶ added in v0.11.0
func (op *NodeByIndexSeek) PlanDetail() string
PlanDetail reports the value this seek looks up, which is the whole point of the access path: an index seek is only as selective as its key.
type NodeByIndexSeekSet ¶ added in v0.11.0
type NodeByIndexSeekSet struct {
// contains filtered or unexported fields
}
NodeByIndexSeekSet is a Volcano leaf operator that performs an equality lookup on a property hash index for each of several keys, emitting each matching NodeID exactly once. Each Row has a single column: expr.IntegerValue(nodeID).
func NewNodeByIndexSeekSet ¶ added in v0.11.0
func NewNodeByIndexSeekSet(idx HashLookup, keys []expr.Value, budget uint64) *NodeByIndexSeekSet
NewNodeByIndexSeekSet creates an operator that looks up every key in idx.
Duplicate keys in keys are harmless — they are probed once. A budget of 0 disables the over-budget check; any other value caps the merged posting count, above which Init reports ErrSeekSetOverBudget.
func (*NodeByIndexSeekSet) Admitting ¶ added in v0.11.0
func (op *NodeByIndexSeekSet) Admitting(admit func(nodeID uint64) bool) *NodeByIndexSeekSet
Admitting installs the residual predicate every candidate must pass, and returns op so a builder can chain it onto the constructor. A nil admit is a no-op. See the admit field (rmp #2423).
func (*NodeByIndexSeekSet) Close ¶ added in v0.11.0
func (op *NodeByIndexSeekSet) Close() error
Close releases the operator. The merged id run is retained for reuse across Init calls, matching NodeByIndexSeek.
func (*NodeByIndexSeekSet) Init ¶ added in v0.11.0
func (op *NodeByIndexSeekSet) Init(ctx context.Context) error
Init probes the index once per distinct key and merges the results into one ascending, duplicate-free run.
A key whose type the index cannot serve is SKIPPED rather than failing the query. That is a correctness requirement, not leniency: openCypher equality across type groups is FALSE, so a key that cannot be in this index matches nothing, and contributing nothing is the right answer. Failing instead would turn `WHERE n.name IN ['a', 7]` into an error where the specification asks for the rows matching 'a'.
type NodeByLabelScan ¶
type NodeByLabelScan struct {
// contains filtered or unexported fields
}
NodeByLabelScan is a Volcano leaf operator that emits one Row per NodeID carrying the named label. Each Row has a single column: expr.IntegerValue(nodeID).
NodeByLabelScan is NOT safe for concurrent use.
func NewNodeByLabelIntersectionScan ¶ added in v0.11.0
func NewNodeByLabelIntersectionScan(labels []string, src LabelIntersectResolver) *NodeByLabelScan
NewNodeByLabelIntersectionScan creates a scan over the INTERSECTION of labels — the set-at-a-time answer to `MATCH (n:A:B)` (#2133).
Only the bitmap Init resolves differs from the single-label form: Next, the columnar FillChunk path, the exact rowCountHint and Close all operate on whatever bitmap the scan holds, so the conjunction inherits the zero-alloc contract and the columnar fast path unchanged.
labels must be ordered as the caller wants them intersected (smallest first — see LabelIntersectResolver) and must contain at least two entries; a single-label conjunction is the plain scan and should use NewNodeByLabelScan.
func NewNodeByLabelScan ¶
func NewNodeByLabelScan(labelName string, src labelResolver) *NodeByLabelScan
NewNodeByLabelScan creates a NodeByLabelScan for the given label.
func (*NodeByLabelScan) Close ¶
func (op *NodeByLabelScan) Close() error
Close releases resources held by the operator.
func (*NodeByLabelScan) FillChunk ¶ added in v0.9.0
func (op *NodeByLabelScan) FillChunk(dst *Chunk, maxRows int) (int, error)
FillChunk appends up to maxRows more matching NodeIDs, as unboxed int64, into column 0 of dst and returns the number appended (0 at end-of-stream). It is the column-major counterpart of NodeByLabelScan.Next: the SAME bitmap iterator in the SAME order, but written to a typed column with no per-row heap box. Only one of Next/FillChunk drives a given query, so sharing op.iter/op.count between them is sound. It honours context cancellation. It implements ChunkProducer.
func (*NodeByLabelScan) Init ¶
func (op *NodeByLabelScan) Init(ctx context.Context) error
Init resolves the label (or, for the conjunction form, the intersection of the labels) to a bitmap and initialises the iterator.
func (*NodeByLabelScan) NewOutputChunk ¶ added in v0.9.0
func (op *NodeByLabelScan) NewOutputChunk(capacity int) *Chunk
NewOutputChunk returns a Chunk with a single static integer column that NodeByLabelScan fills with unboxed NodeIDs. It implements ChunkProducer (#1704 P3): a columnar-aware parent drains the scan column-major, avoiding the per-row expr.Value box NodeByLabelScan.Next pays.
func (*NodeByLabelScan) Next ¶
func (op *NodeByLabelScan) Next(out *Row) (bool, error)
Next emits the next matching NodeID. Returns (false, nil) at end-of-stream.
func (*NodeByLabelScan) PlanDetail ¶ added in v0.11.0
func (op *NodeByLabelScan) PlanDetail() string
PlanDetail reports the label this scan iterates, or — for the multi-label conjunction form (#2133) — the intersected labels in the order they are ANDed, which is the order the planner chose by ascending cardinality and is therefore part of the physical decision a reader needs to see.
type NodeIDColumnProducer ¶ added in v0.9.0
type NodeIDColumnProducer interface {
ChunkProducer
// contains filtered or unexported methods
}
NodeIDColumnProducer is a ChunkProducer whose output chunk carries, at every column that corresponds to a bound node variable, that node's raw int64 NodeID (unboxed) — the "scan row shape". The columnar projection's chunk-input fast path (#1704 P3) reads NodeIDs directly from such a producer via Chunk.Int64. AllNodesScan, NodeByLabelScan, and ColumnarFilter (a passthrough over a scan) implement it; ColumnarProject deliberately does NOT — it emits projected values, not raw NodeIDs, so a projection over a projection takes the boxed row path. The marker method is unexported, so the interface is sealed to this package: a consumer in another package can assert against it but cannot forge a non-scan-shaped producer into it.
type NumericPointLookup ¶ added in v0.11.0
NumericPointLookup is the minimal capability this operator needs from the numeric btree companion: an allocation-free ascending point lookup.
It is deliberately narrower than [rangeLookup], whose RangeBitmap allocates a roaring bitmap per call. This operator performs one lookup per OUTER ROW, so a per-call allocation would be charged B times; LookupAppend reuses the caller's buffer instead. btree.Index[float64] satisfies it directly.
type Operator ¶
type Operator interface {
// Init initialises the operator and its children. ctx is stored for later
// use in Next; implementations must not begin producing rows in Init.
Init(ctx context.Context) error
// Next advances the operator by one row, writing the result into out.
// It returns (true, nil) if a row was written, (false, nil) at end-of-stream,
// or (false, err) on error. After returning (false, _), Next must not be
// called again.
//
// Implementations check ctx.Done() on every call. Long-running loops check
// ctx.Done() every 4096 iterations.
Next(out *Row) (bool, error)
// Close releases all resources held by this operator (open file handles,
// memory, goroutines). It must be called exactly once by the pipeline
// driver, even when Next returned an error.
Close() error
}
Operator is the core abstraction of the Volcano iterator model. Every node in a physical query plan implements this interface.
Lifecycle ¶
- [Init] is called exactly once before the first call to [Next].
- [Next] is called repeatedly until it returns (false, nil) or an error.
- [Close] is called exactly once, regardless of whether [Next] returned an error. Implementations must release all resources in [Close].
Cancellation ¶
Every [Next] implementation must check ctx.Done() at the top of the call. For long-running inner loops that produce more than 4096 tuples without returning, check ctx.Done() every 4096 iterations.
Concurrency ¶
An Operator instance is NOT safe for concurrent use. Each goroutine in a parallel pipeline segment owns its own operator tree.
type OptionalApply ¶
type OptionalApply struct {
// contains filtered or unexported fields
}
OptionalApply is the left-outer variant of CorrelatedApply. For every outer row, the inner pipeline is driven exactly like CorrelatedApply; when the inner pipeline produces zero rows for a given outer row, OptionalApply emits a single NULL-extended row whose width equals the configured padded width, holding the outer columns followed by NULL placeholders for the inner-introduced columns.
OptionalApply is NOT safe for concurrent use.
func NewOptionalApply ¶
func NewOptionalApply(outer, inner Operator, arg *Argument, paddedWidth int) *OptionalApply
NewOptionalApply creates an OptionalApply operator.
- outer is the left (driving) plan.
- inner is the right (sub) plan whose leftmost leaf is the provided arg.
- arg is the Argument node at the inner leaf; OptionalApply seeds it before each inner Init call.
- paddedWidth is the total width of an output row, i.e. outerWidth plus the number of columns the inner pipeline introduces. When the inner pipeline emits zero rows for an outer, OptionalApply emits a row of this width whose first outerWidth columns are the outer row and whose trailing columns are expr.Null.
OptionalApply takes ownership of both plans.
func (*OptionalApply) Close ¶
func (op *OptionalApply) Close() error
Close releases resources and closes both the outer and inner plans.
func (*OptionalApply) Init ¶
func (op *OptionalApply) Init(ctx context.Context) error
Init initialises both the outer plan and stores ctx for subsequent Next calls.
func (*OptionalApply) Next ¶
func (op *OptionalApply) Next(out *Row) (bool, error)
Next emits the next output row. The semantics are:
- For each outer row, drain the inner pipeline.
- If the inner pipeline emits ≥1 row, those rows are forwarded verbatim.
- If the inner pipeline emits 0 rows, a single NULL-extended row is emitted consisting of the outer columns followed by expr.Null for every inner column the pipeline would have introduced.
func (*OptionalApply) PlanChildren ¶ added in v0.11.0
func (op *OptionalApply) PlanChildren() []Operator
PlanChildren reports the outer input that drives this operator, then the inner sub-plan it re-drives for each outer row.
type OptionalExpand ¶
type OptionalExpand struct {
// contains filtered or unexported fields
}
OptionalExpand is a Volcano pipeline operator that performs a single-hop expansion and emits a NULL-extended row when no edges match for an input node.
OptionalExpand is NOT safe for concurrent use.
func NewOptionalExpand ¶
func NewOptionalExpand(input Operator, src AdjacencySource, cfg ExpandConfig) *OptionalExpand
NewOptionalExpand creates an OptionalExpand operator.
- input is the upstream operator supplying node IDs.
- src yields the forward and reverse adjacency at execution time; see AdjacencySource.
- cfg is the Expand configuration (direction, inputCol).
The NULL-extension row uses the same column layout as Expand: inputRow... || srcID || Null(edgeID) || Null(dstID).
func (*OptionalExpand) Close ¶
func (op *OptionalExpand) Close() error
Close closes the input and child operators.
func (*OptionalExpand) Init ¶
func (op *OptionalExpand) Init(ctx context.Context) error
Init initialises the operator.
func (*OptionalExpand) Next ¶
func (op *OptionalExpand) Next(out *Row) (bool, error)
Next emits the next row. For each input row:
- It feeds the row into the inner Expand one hop at a time.
- If Expand emits ≥1 row, those rows are forwarded as-is.
- If Expand emits 0 rows for an input node, a NULL-extended row is emitted.
func (*OptionalExpand) PlanChildren ¶ added in v0.11.0
func (op *OptionalExpand) PlanChildren() []Operator
PlanChildren reports the upstream input first, then the Expand it wraps.
OptionalExpand holds both its upstream input and the Expand it re-drives one row at a time (through singleArg). Both are real operators in the executed pipeline, so both are reported; input comes first because it drives.
type ParallelAggregateScan ¶ added in v0.10.0
type ParallelAggregateScan struct {
// contains filtered or unexported fields
}
ParallelAggregateScan is a Volcano leaf operator that partitions a full-node scan into contiguous morsels, accumulates per-worker count/min/max partials over each morsel's pre-aggregation rows, and combines them into the final aggregation result with a deterministic, byte-identical-to-serial combine.
ParallelAggregateScan is NOT safe for concurrent use.
func NewParallelAggregateScan ¶ added in v0.10.0
func NewParallelAggregateScan(g nodeWalker, factory AggInputFactory, nKeys int, reducers []AggReducerKind, morselSize int, gov *ParallelGovernor) *ParallelAggregateScan
NewParallelAggregateScan creates a ParallelAggregateScan over g. nKeys is the number of leading grouping-key columns in each pre-aggregation row (0 ⇒ a global aggregate); reducers gives the combine for each aggregate column (in order, at row columns nKeys+i). factory rebuilds the per-worker pre-aggregation sub-plan. morselSize controls the chunk size per worker (0 ⇒ DefaultMorselSize); gov is the engine-shared worker-budget governor (nil ⇒ unbounded GOMAXPROCS).
func (*ParallelAggregateScan) Close ¶ added in v0.10.0
func (op *ParallelAggregateScan) Close() error
Close cancels any still-running workers and joins them. It is idempotent and safe whether or not Next was ever called.
func (*ParallelAggregateScan) Init ¶ added in v0.10.0
func (op *ParallelAggregateScan) Init(ctx context.Context) error
Init collects all live node IDs on the calling goroutine (the ONLY phase that touches graph state), partitions them into contiguous morsels tagged with their base offset, and launches the workers. Each worker accumulates private count/min/max partials over the morsels it dequeues. The join and combine are deferred to the first Next so every worker is joined on the Next goroutine, inside the engine's visibility barrier.
func (*ParallelAggregateScan) Next ¶ added in v0.10.0
func (op *ParallelAggregateScan) Next(out *Row) (bool, error)
Next emits the aggregation result. The first call joins every worker (wg.Wait), surfaces the first worker error, combines the per-worker partials, and builds the output rows; subsequent calls stream them.
func (*ParallelAggregateScan) WithByteBudget ¶ added in v0.10.0
func (op *ParallelAggregateScan) WithByteBudget(maxBytes int64, estimateRow func(Row) int64) *ParallelAggregateScan
WithByteBudget bounds the estimated retained size of the grouping keys by maxBytes (per worker and on the merged map), the group-by analogue of the serial EagerAggregation.WithByteBudget (#1841). A non-positive maxBytes or a nil estimateRow leaves the byte dimension disabled. Returns op for chaining; call before Init.
func (*ParallelAggregateScan) WithGroupCap ¶ added in v0.10.0
func (op *ParallelAggregateScan) WithGroupCap(maxGroups int) *ParallelAggregateScan
WithGroupCap sets the maximum distinct group count (per worker and on the merged map); 0 keeps DefaultMaxGroups. Returns op for chaining; call before Init.
type ParallelCountScan ¶ added in v0.6.0
type ParallelCountScan struct {
// contains filtered or unexported fields
}
ParallelCountScan is a Volcano leaf operator that computes a group-by-less count over a full node scan using per-worker partial counters combined once at the end. It emits exactly one row with a single expr.IntegerValue column carrying the total live-node count.
ParallelCountScan is NOT safe for concurrent use.
func NewParallelCountScan ¶ added in v0.6.0
func NewParallelCountScan(g nodeWalker, morselSize int, gov *ParallelGovernor) *ParallelCountScan
NewParallelCountScan creates a ParallelCountScan over g. morselSize controls the chunk size per worker; pass 0 to use DefaultMorselSize. gov is the engine-shared adaptive worker-budget governor (nil = unbounded GOMAXPROCS).
func (*ParallelCountScan) Close ¶ added in v0.6.0
func (op *ParallelCountScan) Close() error
Close cancels any still-running workers and joins them. It is idempotent and safe whether or not Next was ever called: wg.Wait returns immediately once the workers have drained, and cancel unblocks a worker stalled on ctx.
func (*ParallelCountScan) Init ¶ added in v0.6.0
func (op *ParallelCountScan) Init(ctx context.Context) error
Init collects all node IDs, partitions them into morsels, and launches worker goroutines that each accumulate a private count. The combine is deferred to the first Next call so every worker is joined on the Next goroutine, inside the engine's visibility barrier.
func (*ParallelCountScan) Next ¶ added in v0.6.0
func (op *ParallelCountScan) Next(out *Row) (bool, error)
Next emits the single aggregated row on its first call. It joins every worker synchronously (wg.Wait) on the calling goroutine, then sums the per-worker partials. Subsequent calls report end-of-stream.
type ParallelGovernor ¶ added in v0.6.0
type ParallelGovernor struct {
// contains filtered or unexported fields
}
ParallelGovernor bounds intra-query parallelism across the concurrently executing queries that share one Engine. A morsel-parallel leaf registers on Init via ParallelGovernor.Enter (which returns its worker budget) and deregisters on Close via ParallelGovernor.Leave.
The budget is GOMAXPROCS divided by the number of parallel leaves currently in flight (including the caller), clamped to [1, morsels]:
- a single parallel query in flight gets the full GOMAXPROCS workers — no change from the prior unconditional behaviour, so single-query latency is unaffected;
- N concurrent parallel queries each get ~GOMAXPROCS/N workers, so the aggregate worker count stays near GOMAXPROCS and the scheduler/cache thrash of N×GOMAXPROCS goroutines is avoided.
The inflight count is sampled once per Enter and races at the edges (an early query may briefly see a low count and grab a large budget); this is a deliberate approximation — it need only prevent the sustained N×GOMAXPROCS explosion, not divide the machine exactly. Worker count never affects results, only timing, so the governor has no ACID or openCypher-TCK impact.
A nil *ParallelGovernor is valid and means "unbounded" — every leaf gets the full GOMAXPROCS budget and no count is tracked. This preserves the prior behaviour for any caller that constructs an operator without a governor (the public BuildPlan path and the operator unit tests).
ParallelGovernor is safe for concurrent use by any number of goroutines.
func (*ParallelGovernor) Enter ¶ added in v0.6.0
func (g *ParallelGovernor) Enter(morsels int) int
Enter registers one parallel leaf and returns its worker budget, an integer in [1, morsels]. It MUST be paired with exactly one ParallelGovernor.Leave (deferred in the operator's Close). On a nil governor it returns the full GOMAXPROCS budget (clamped to morsels) without tracking anything.
morsels is the number of work units the leaf has to distribute; the budget is never larger than that (more workers than morsels would leave workers idle).
func (*ParallelGovernor) Leave ¶ added in v0.6.0
func (g *ParallelGovernor) Leave()
Leave deregisters a parallel leaf previously registered via ParallelGovernor.Enter. It is nil-safe and idempotency is the caller's responsibility (call it exactly once per successful Enter).
type ParallelScanProject ¶ added in v0.6.0
type ParallelScanProject struct {
// contains filtered or unexported fields
}
ParallelScanProject is a Volcano leaf operator that partitions a full-node scan into morsels, runs an independent fused scan→filter→project sub-plan per morsel on up to GOMAXPROCS workers, and emits the concatenated per-worker result rows. The output schema is the projection's output schema (set by the factory's Project), so this operator's rows are ready for the engine's final column passthrough.
ParallelScanProject is NOT safe for concurrent use.
func NewParallelScanProject ¶ added in v0.6.0
func NewParallelScanProject(g nodeWalker, factory SubplanFactory, morselSize int, gov *ParallelGovernor) *ParallelScanProject
NewParallelScanProject creates a ParallelScanProject over g whose per-worker fused sub-plans are built by factory. morselSize controls the chunk size per worker; pass 0 to use DefaultMorselSize.
func (*ParallelScanProject) Close ¶ added in v0.6.0
func (op *ParallelScanProject) Close() error
Close cancels any still-running workers and joins them. It is idempotent and safe whether or not Next was ever called: wg.Wait returns immediately once the workers have drained, and cancel unblocks a worker stalled on ctx or inside a sub-plan's Next.
func (*ParallelScanProject) Init ¶ added in v0.6.0
func (op *ParallelScanProject) Init(ctx context.Context) error
Init collects all node IDs, partitions them into morsels, builds one independent sub-plan per worker on the calling goroutine, and launches the workers. Each worker drives its sub-plan over the morsels it dequeues and accumulates deep-copied result rows into its private buffer. The join and combine are deferred to the first Next call so every worker is joined on the Next goroutine, inside the engine's visibility barrier.
func (*ParallelScanProject) Next ¶ added in v0.6.0
func (op *ParallelScanProject) Next(out *Row) (bool, error)
Next streams the concatenated per-worker result rows. The first call joins every worker synchronously (wg.Wait) on the calling goroutine, surfaces the first worker error if any, then concatenates the per-worker buffers. Subsequent calls advance the cursor through the concatenation.
func (*ParallelScanProject) WithResultBudget ¶ added in v0.7.0
func (op *ParallelScanProject) WithResultBudget(maxRows, maxBytes int64, estimateRow func(Row) int64) *ParallelScanProject
WithResultBudget threads the engine's per-query result-memory budget into the operator so the morsel workers stop accumulating once the fleet-wide total exceeds maxRows or maxBytes, bounding peak memory on the parallel path (#1830). maxRows/maxBytes of 0 leave that dimension unbounded (the engine convention); estimateRow is the engine's coarse per-row byte estimate and may be nil to disable byte-budget enforcement. It returns op for chaining and must be called before Init. When neither bound is set the operator behaves exactly as before (full materialisation), so the result multiset is unchanged under budget.
type PlanChildren ¶ added in v0.11.0
type PlanChildren interface {
PlanChildren() []Operator
}
PlanChildren is implemented by every operator that draws rows from other operators. An operator that does not implement it is rendered as a leaf.
It exists because an operator's inputs are held in UNEXPORTED fields, which reflection cannot read: `reflect.Value.Interface` panics on an unexported field, so the plan tree cannot be recovered by inspection alone. The method is therefore the structural contract, and a source-level completeness gate (TestPlanChildren_EveryOperatorWithInputsImplementsIt) fails the build if an operator that holds an input forgets it — otherwise a rendered plan would silently truncate at that node, which is the class of defect this whole surface exists to remove.
Return the inputs in EXECUTION order, which for an asymmetric operator is the order that explains the cost: a join returns its build side before its probe side, an apply its outer before its inner.
type PlanDetail ¶ added in v0.11.0
type PlanDetail interface {
PlanDetail() string
}
PlanDetail is implemented by operators that took a physical decision worth showing next to their name — the label they scan, the index they seek, the tier they engaged. It is optional: an operator without it renders as its name alone.
Keep the string short and factual; it is appended in square brackets after the operator name.
type PlanNode ¶ added in v0.11.0
type PlanNode struct {
// Name is the concrete operator type name, e.g. "HashJoin".
Name string
// Detail is the operator's own [PlanDetail], empty when it has none.
Detail string
// Children are the operator's inputs, in execution order.
Children []PlanNode
// Rows is the number of rows this operator emitted, and Time the wall-clock
// time attributed to its own Next calls. Both are zero unless the plan was
// captured by a profiling run ([Profiler]); Profiled records which.
Rows int64
Time time.Duration
Profiled bool
// DbHits is the number of logical storage record accesses attributed to this
// operator — the measure that distinguishes a selective seek from a scan that
// filtered afterwards, since both can emit the same few rows while touching
// wildly different amounts of storage (rmp #2238).
//
// It is counted only where it can be counted EXACTLY: at the operators that
// read records from storage, one hit per record read (see [StorageRecordScan]).
// An operator that only transforms rows its children produced reports 0,
// because it accessed no storage. That convention is the one the in-tree
// db-hits work established (T910/T913) and it is a documented DIVERGENCE from
// Neo4j, which additionally charges a hit per property read; see docs/cypher.md.
DbHits int64
}
PlanNode is one operator in a rendered physical plan.
Name is the operator's CONCRETE Go type name, taken from the value itself, so it cannot disagree with the operator that runs. That is the property the plan surface turns on: a HashJoin substituted for a nested loop is named HashJoin because it IS a *HashJoin, not because a second reconstruction of the planner's decisions happened to agree (rmp #2222).
func PlanTree ¶ added in v0.11.0
PlanTree builds the physical plan tree rooted at op.
It follows PlanChildren for structure and reads each node's name from its concrete type. When op is a profiling wrapper the wrapper is transparent: the node carries the wrapped operator's name with the wrapper's measurements.
type ProcedureCallOp ¶
type ProcedureCallOp struct {
// contains filtered or unexported fields
}
ProcedureCallOp invokes a registered procedure and emits its result rows.
ProcedureCallOp is NOT safe for concurrent use.
func NewProcedureCallOp ¶
func NewProcedureCallOp( namespace []string, name string, argExprs []func(Row) (expr.Value, error), yieldVars []string, child Operator, reg *procs.Registry, ) *ProcedureCallOp
NewProcedureCallOp creates a ProcedureCallOp.
namespace and name identify the procedure. argExprs evaluate procedure arguments against the current driver row. yieldVars names the output columns. child is the driving subplan; pass nil for a standalone CALL. reg is the procedure registry used for lookup at runtime.
func (*ProcedureCallOp) Close ¶
func (op *ProcedureCallOp) Close() error
Close releases resources and closes the child operator.
func (*ProcedureCallOp) Init ¶
func (op *ProcedureCallOp) Init(ctx context.Context) error
Init resets internal state and initialises the child if present.
func (*ProcedureCallOp) Next ¶
func (op *ProcedureCallOp) Next(out *Row) (bool, error)
Next advances the operator by one row.
It draws driving rows from the child (or a synthetic empty row when child is nil), invokes the procedure for each, buffers all result rows, and emits them one at a time.
Void procedure semantics. A procedure declared with no output columns (len(yieldVars) == 0) is treated as a side-effect-only step. When invoked in-query (op.child != nil) it must NOT consume the driver row; instead each driver row is emitted unchanged once the impl has run, preserving the upstream variable bindings for any downstream RETURN. Standalone CALL (op.child == nil) emits nothing.
func (*ProcedureCallOp) PlanChildren ¶ added in v0.11.0
func (op *ProcedureCallOp) PlanChildren() []Operator
PlanChildren reports the input whose rows drive the procedure call.
type Profiler ¶ added in v0.11.0
type Profiler struct {
// contains filtered or unexported fields
}
Profiler captures per-operator measurements for one query execution.
Cost when off ¶
A Profiler is opt-in and there is exactly one place that installs it: the recursive plan builder wraps each operator it returns, and only when a Profiler is present. With none, no wrapper is built and no operator executes any instrumentation — the normal path runs byte-identical code to a build that never had profiling. That is the acceptance condition (rmp #2222 AC 3) and the reason the counters live in a wrapper rather than in the operators: an `if p != nil` inside 55 Next implementations would be a cost on every row of every query forever.
Why the wrapper must be transparent ¶
The builder wraps on the way OUT of its recursion, so a parent is constructed with its child ALREADY wrapped and runs its capability type-assertions against the wrapper. A wrapper that hid ChunkProducer would make the parent build a row-mode operator instead of a columnar one, so profiling would change the very plan it exists to observe. Wrap therefore returns a variant matching what the wrapped operator exposes:
- plain — Operator only
- chunk — also ChunkProducer
- nodeID — also NodeIDColumnProducer
All variants forward rowCountHint, which is likewise asserted on children (to bound allocation) and whose contract already covers "no bound known". TestProfile_PlanShapeIsIdenticalProfiledOrNot is the gate on that transparency: it compares the rendered tree with and without a Profiler.
Why this is not cypher/explain.ProfiledOperator ¶
The cypher/explain package already had a ProfiledOperator recording rows and elapsed time (plus a DbHits counter), written before any engine surface existed to wire it to. It could not be the wiring, for a reason that is structural rather than stylistic: transparency requires the wrapper to re-implement NodeIDColumnProducer, whose identifying method nodeIDColumnProducer() is UNEXPORTED to this package. A wrapper declared anywhere else cannot satisfy that interface, so wrapping from cypher/explain would strip the marker and silently downgrade a columnar plan to row mode.
The measurements therefore live here; cypher/explain keeps its DbHits counter and report formatter, and folding db-hits into this Profiler is tracked separately (rmp #2238) since it needs a counter threaded through the storage accessors rather than a wrapper.
Concurrency ¶
A Profiler is NOT safe for concurrent use and must not be shared between queries. One query's pipeline is driven by one goroutine, so the wrappers need no synchronisation. An operator that fans work out internally (the parallel tier) is measured as one node: the wrapper times the calls the driving goroutine makes, which is the honest attribution — it cannot see inside.
func NewProfiler ¶ added in v0.11.0
func NewProfiler() *Profiler
NewProfiler returns a Profiler ready to instrument one query build.
type Project ¶
type Project struct {
// contains filtered or unexported fields
}
Project is a Volcano pipeline operator that applies a list of ProjectionItem expressions to each input row, producing an output row with one column per item.
Project is NOT safe for concurrent use.
func NewProject ¶
func NewProject(child Operator, items []ProjectionItem) (*Project, error)
NewProject creates a Project operator. items defines the output schema; each item's Eval function is applied to each input row. An empty items slice is legal (e.g. `WITH *` over a pattern that binds no variables); the resulting operator forwards an empty Row for every input row.
func (*Project) Next ¶
Next evaluates each projection item against the next input row and writes the result into out. Returns (true, nil) when a projected row is available, (false, nil) at end-of-stream, or (false, err) on evaluation or child error.
func (*Project) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input it projects.
func (*Project) WithRowByteBudget ¶ added in v0.7.0
func (op *Project) WithRowByteBudget(maxRowBytes int64, estimateValue func(expr.Value) int64) *Project
WithRowByteBudget bounds the estimated size of a single assembled output row by maxRowBytes, using estimateValue for the per-column estimate. It is enforced INCREMENTALLY inside Next — after each column is evaluated, before the next — so a projection of several large columns (e.g. RETURN range(1,N), range(1,N), …) is rejected before the whole row is materialised, bounding the transient peak to maxRowBytes plus one column regardless of the column count. It complements the drain's aggregate per-result byte budget, which is a retention guard on the SUM of already-built rows and therefore fires only after Next has assembled the row; this per-row guard moves the same accounting earlier and makes it per-column so construction cannot OOM (#1852). A non-positive maxRowBytes or nil estimateValue leaves the guard disabled (behaviour-preserving). Returns op for chaining; call before Init.
type ProjectionItem ¶
type ProjectionItem struct {
// Eval evaluates the item expression against the current input row and
// returns the projected value.
Eval func(Row) (expr.Value, error)
// Alias is the output column name (e.g. "n", "count(n)", "x").
Alias string
}
ProjectionItem describes a single column in a projection. Eval is evaluated against the input row; Alias names the resulting output column.
type PropEntry ¶
type PropEntry struct {
Value lpg.PropertyValue
Key string
}
PropEntry is an exported key/value pair for use by external plan builders (api.go) that construct dynamic property evaluators. It mirrors propLiteral but carries exported fields so the physical builder can return values from a PropsEvalFn without requiring propLiteral to be exported.
type PropsEvalFn ¶
PropsEvalFn is a per-row property evaluator closure. It receives the current row and returns a slice of (key, value) pairs produced by evaluating the property-map AST expressions against the row's bound variables. Any entry whose evaluation yields Null is omitted (openCypher: assigning null to a property is a no-op on a fresh node).
It returns a non-nil error to fail-stop the statement — a runtime evaluation error, or a value that is not a valid property (a map or nested collection, InvalidPropertyType) — rather than silently dropping the entry (audit 2026-07-13 security F3 / fail-stop mandate).
The closure is constructed once by the physical plan builder and captures the schema, function registry, and query parameters.
type QueryCounters ¶ added in v0.11.0
type QueryCounters struct {
NodesCreated int64
NodesDeleted int64
RelationshipsCreated int64
RelationshipsDeleted int64
PropertiesSet int64
PropertiesRemoved int64
LabelsAdded int64
LabelsRemoved int64
IndexesAdded int64
IndexesRemoved int64
ConstraintsAdded int64
ConstraintsRemoved int64
}
QueryCounters holds the write effects one Cypher statement actually applied.
Semantics ¶
Every counter records an effect that was ACTUALLY APPLIED, incremented by the write adapter at the point it already discriminates a real change from a no-op:
- a re-intern of an existing node is not a creation, but re-creating a TOMBSTONED key is (the adapter's existing rule);
- REMOVE of an absent property, and removal of an absent label, count nothing;
- a MERGE that matched counts nothing, because it never reaches a create.
A statement that fails or is rolled back must report nothing: the counters live on the per-statement adapter, so an aborted statement's instance is simply discarded.
Concurrency ¶
QueryCounters is NOT safe for concurrent use, and deliberately holds plain integers rather than atomics. The reason is OWNERSHIP, not serialisation: each physical operator tree owns exactly one write adapter and the adapter owns its counters inline, so concurrent statements never share a QueryCounters and there is nothing to synchronise. Adding atomics would cost the write path for nothing.
This note used to give the reason as "the Cypher write path is single-writer", which stopped being true when rmp #2306 retired the writer serialisers. The safety survived because it never rested on that; pinned by cypher.TestConcurrentCreate_PerStatementCountersAreNotShared.
func (*QueryCounters) Add ¶ added in v0.11.0
func (c *QueryCounters) Add(other *QueryCounters)
Add folds other into c. It is used to accumulate an explicit transaction's effects across the statements it contains. A nil receiver or argument is a no-op.
func (*QueryCounters) ContainsUpdates ¶ added in v0.11.0
func (c *QueryCounters) ContainsUpdates() bool
ContainsUpdates reports whether the statement changed anything at all — the value Bolt's contains-updates carries and the driver surfaces as ResultSummary.Counters().ContainsUpdates().
It is derived rather than tracked so it cannot disagree with the counters: any non-zero effect makes it true.
type RangeBound ¶
type RangeBound struct {
// Value is the bound's expr.Value. Nil means unbounded (use the
// minimum or maximum representable value for the index type).
Value expr.Value
// Include records the caller's intended inclusivity (≤ / ≥ vs < / >). It is
// metadata only: NodeByIndexRangeScan always emits the inclusive [lo, hi]
// superset and relies on a residual predicate Filter for exact open/closed
// semantics (see the NodeByIndexRangeScan type doc, #F-EXEC1).
Include bool
}
RangeBound carries one endpoint of a range predicate.
type Record ¶
type Record map[string]interface{}
Record is a single result row, accessed by column name. The underlying map is owned by the ResultSet; callers must copy values they need to retain beyond the next ResultSet.Next call.
type RelCols ¶
RelCols carries the raw column indices that the Expand operator places for a relationship variable. SetProperty and RemoveProperty use it to reconstruct the (src, dst) endpoint keys when the bound entity is a relationship rather than a node.
SrcCol and DstCol hold the endpoint NodeIDs as IntegerValue. EdgeCol holds the forward-CSR edge-position counter (= schema[entityVar]); it is used to resolve the bound parallel instance's stable handle via [GraphMutator.EdgeHandleAtPosition] so a relationship SET/REMOVE maintains the per-instance by-handle property store (#1686). EdgeCol may be 0 only when it genuinely is column 0; callers distinguish "no handle" by the resolver returning 0, not by the column index.
type RelEndpointFn ¶
RelEndpointFn returns the (srcID, dstID) endpoints for an edge that the schema-direct path is about to delete. Used when the bare-variable target carries an IntegerValue edge id (the in-pipeline encoding emitted by Expand) so DeleteNode can dispatch to the edge-removal branch without misinterpreting the id as a node id.
type RemoveLabels ¶
type RemoveLabels struct {
// contains filtered or unexported fields
}
RemoveLabels removes one or more labels from an already-bound node per input row.
Detaching a label takes the node OUT of every UNIQUE constraint declared on that label, so the operator releases the node's reservation for each constrained property before it writes — without that, the value stays reserved for ever and a later legitimate write of it is refused by a phantom. See cypher/exec/label_constraints.go. Enforcement is inert unless a UNIQUE constraint is registered.
RemoveLabels is NOT safe for concurrent use.
func NewRemoveLabels ¶
func NewRemoveLabels( nodeVar string, labels []string, schema map[string]int, child Operator, mutator GraphMutator, ) *RemoveLabels
NewRemoveLabels creates a RemoveLabels operator.
func (*RemoveLabels) Init ¶
func (op *RemoveLabels) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*RemoveLabels) Next ¶
func (op *RemoveLabels) Next(out *Row) (bool, error)
Next pulls one row from the child and removes the specified labels.
func (*RemoveLabels) PlanChildren ¶ added in v0.11.0
func (op *RemoveLabels) PlanChildren() []Operator
PlanChildren reports the input whose rows name the nodes it relabels.
func (*RemoveLabels) WithConstraintRegistry ¶ added in v0.11.0
func (op *RemoveLabels) WithConstraintRegistry(reg *ConstraintRegistry) *RemoveLabels
WithConstraintRegistry attaches a ConstraintRegistry so RemoveLabels releases the unique-constraint reservations the detached labels free. Returns op for chaining. No index.Manager is needed: releasing consults only the registry's own value-set, never the backing hash index.
type RemoveProperty ¶
type RemoveProperty struct {
// contains filtered or unexported fields
}
RemoveProperty removes a single named property from an already-bound node or relationship per input row. For relationships, call WithRelCols to supply the endpoint column indices.
RemoveProperty is NOT safe for concurrent use.
func NewRemoveProperty ¶
func NewRemoveProperty( entityVar, propertyKey string, schema map[string]int, child Operator, mutator GraphMutator, ) *RemoveProperty
NewRemoveProperty creates a RemoveProperty operator.
func (*RemoveProperty) Close ¶
func (op *RemoveProperty) Close() error
Close closes the child operator.
func (*RemoveProperty) Init ¶
func (op *RemoveProperty) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*RemoveProperty) Next ¶
func (op *RemoveProperty) Next(out *Row) (bool, error)
Next pulls one row from the child and removes the specified property.
func (*RemoveProperty) PlanChildren ¶ added in v0.11.0
func (op *RemoveProperty) PlanChildren() []Operator
PlanChildren reports the input whose rows name the entities it strips.
func (*RemoveProperty) WithConstraintRegistry ¶ added in v0.3.0
func (op *RemoveProperty) WithConstraintRegistry(reg *ConstraintRegistry) *RemoveProperty
WithConstraintRegistry attaches a ConstraintRegistry so RemoveProperty releases unique-constraint value reservations when a node property is removed. Returns op for chaining.
func (*RemoveProperty) WithRelCols ¶
func (op *RemoveProperty) WithRelCols(rc RelCols) *RemoveProperty
WithRelCols marks entityVar as a relationship variable and records the row columns that hold the src and dst NodeIDs. Must be called before the first Next invocation. Returns op for chaining.
type Result ¶
type Result interface {
// Next advances to the next result row. It returns true if a row is
// available, false at end-of-stream or on error. After Next returns false,
// callers should check Err.
Next() bool
// Record returns the current row as a [Record] (column name → value).
// Record must not be called before the first successful Next or after Next
// returns false.
Record() Record
// Err returns the first error encountered during iteration, or nil.
Err() error
// Columns returns the ordered list of column names for this result set.
// The slice is stable across calls and is not modified after construction.
Columns() []string
// Close releases all resources held by this result set, including the
// underlying operator tree. It must be called exactly once.
Close() error
}
Result is a forward-only, streaming iterator over query result rows.
Lifecycle ¶
- Call [Next] in a loop until it returns false.
- After the loop, check [Err] for any error that terminated iteration.
- Call [Close] exactly once to release resources.
Concurrency ¶
Result implementations are NOT safe for concurrent use.
type ResultSet ¶
type ResultSet struct {
// contains filtered or unexported fields
}
ResultSet is the concrete implementation of Result returned by Run.
ResultSet is NOT safe for concurrent use.
func Run ¶
Run initialises plan, stores the column names, and returns a ResultSet ready for iteration. The caller drives iteration via ResultSet.Next and must call ResultSet.Close when done.
Run does not pull any rows; all work happens lazily in ResultSet.Next. The Record map is allocated lazily on the first ResultSet.Record / ResultSet.TakeRecord call (#1499 follow-up) and then reused across every subsequent Next call. A consumer that drains rows positionally via ResultSet.Row — the materialisation path and the Bolt PULL path — never triggers the allocation at all, which keeps small-result queries off the heap.
func (*ResultSet) Close ¶
Close releases all resources held by the ResultSet, including the underlying operator tree. It must be called exactly once. Calling Close after a previous Close is a no-op.
func (*ResultSet) ColumnarProducer ¶ added in v0.9.0
func (rs *ResultSet) ColumnarProducer() (ChunkProducer, bool)
ColumnarProducer reports whether this ResultSet's plan can produce its output column-major, returning the ChunkProducer when it can. A columnar-aware sink prefers the columnar drain when this returns true; otherwise it drains row-at-a-time via ResultSet.Next.
func (*ResultSet) Columns ¶
Columns returns the ordered list of column names. The slice is never nil and is stable for the lifetime of the ResultSet.
func (*ResultSet) Next ¶
Next advances to the next result row. It returns true when a row is available (accessible via Record), and false at end-of-stream or on error.
func (*ResultSet) PlanChildren ¶ added in v0.11.0
PlanChildren reports the plan this result set drains. ResultSet is the pipeline root the driver pulls from.
func (*ResultSet) Record ¶
Record returns the current row as a map. Must only be called after a successful Next.
The returned map is owned by the ResultSet and reused by the next Next call; callers that need to retain a row beyond the next Next must copy it (or use ResultSet.TakeRecord). The map is built lazily on the first Record call for the current row, so a caller that consumes rows positionally via ResultSet.Row never pays for it.
func (*ResultSet) Row ¶ added in v0.3.1
Row returns the current row as a positional slice of values whose indices correspond to ResultSet.Columns. The slice is owned by the operator tree and is reused on the next ResultSet.Next call; callers that retain values beyond the next Next must copy them. This is the allocation-free accessor: unlike ResultSet.Record it never builds a map. Must only be called after a successful Next.
func (*ResultSet) RowCountHint ¶ added in v0.6.0
RowCountHint reports a best-effort upper bound on the number of rows this ResultSet will yield, valid after Run (which calls Init). It returns ok=false when the plan exposes no sound upper bound. Callers use it purely to presize buffers; the value is never an exact count and must not be used to decide how many rows exist. See [rowCountHinter].
func (*ResultSet) TakeRecord ¶
TakeRecord returns the current row and transfers ownership of its backing map to the caller, installing a fresh map for subsequent Next calls. Unlike ResultSet.Record — whose result is reused on the next Next — the map returned here is safe to retain. The materialisation path uses this to drain rows under the transaction-visibility barrier without the extra per-row copy that re-hashing every column into a new map would cost. Must only be called after a successful Next.
type RollUpApply ¶
type RollUpApply struct {
// contains filtered or unexported fields
}
RollUpApply is a Volcano pipeline operator that performs pattern-comprehension execution: for each outer row, it drains the entire inner sub-plan into a expr.ListValue and emits (outerRow... || listValue) as a single output row.
RollUpApply is NOT safe for concurrent use.
func NewRollUpApply ¶
func NewRollUpApply(outer, inner Operator, arg *Argument, listEval func(Row) (expr.Value, error)) *RollUpApply
NewRollUpApply creates a RollUpApply operator with the default per-list element budget (funcs.DefaultMaxCollectItems).
- outer is the driving (left) plan.
- inner is the correlated (right) sub-plan whose leaf is arg.
- arg is the Argument node seeded with each outer row before inner Init.
- listEval, when non-nil, is called for each inner row to extract the value to collect into the list. When nil, the first column of each inner row is collected.
func NewRollUpApplyN ¶ added in v0.2.0
func NewRollUpApplyN(outer, inner Operator, arg *Argument, listEval func(Row) (expr.Value, error), maxItems int) *RollUpApply
NewRollUpApplyN is NewRollUpApply with an explicit per-list element budget. maxItems uses the EngineOptions.MaxCollectItems encoding so the cap is consistent with the buffering aggregators (#1294): 0 selects funcs.DefaultMaxCollectItems, a negative value disables the cap entirely, and a positive value is used verbatim. The encoding is resolved once here so the drain loop compares against a single non-negative ceiling.
func (*RollUpApply) Close ¶
func (op *RollUpApply) Close() error
Close closes both the outer and inner plans.
func (*RollUpApply) Init ¶
func (op *RollUpApply) Init(ctx context.Context) error
Init initialises the outer plan.
func (*RollUpApply) Next ¶
func (op *RollUpApply) Next(out *Row) (bool, error)
Next emits one output row per outer row. For each outer row, the entire inner plan is drained into a ListValue appended as a new column.
func (*RollUpApply) PlanChildren ¶ added in v0.11.0
func (op *RollUpApply) PlanChildren() []Operator
PlanChildren reports the outer input that drives this operator, then the inner sub-plan it re-drives for each outer row.
type Row ¶
Row is a single tuple in the pipeline: a slice of expr.Value whose positions correspond to the operator's output schema. The slice is owned by the RowSlab that allocated it; callers must not retain it beyond the slab's lifetime.
func Drain ¶
Drain initialises op, pulls every row from the pipeline, and returns the collected rows as a []Row. Close is always called before Drain returns, regardless of whether an error occurred.
Cancellation: Drain honours ctx.Done() via the per-Next check inside each operator. If ctx is cancelled, Drain returns the partial result set and the context error.
The returned rows are independent copies: each element is a snapshot of the Row written by the operator at that iteration. Callers own the returned slice.
Example ¶
ExampleDrain assembles the equivalent of `UNWIND [10, 20, 30] AS x RETURN x LIMIT 2` as a hand-built operator tree and drains it. SingleRow seeds one empty row, Unwind expands the literal list, and Limit caps the output.
package main
import (
"context"
"fmt"
"github.com/FlavioCFOliveira/GoGraph/cypher/exec"
"github.com/FlavioCFOliveira/GoGraph/cypher/expr"
)
func main() {
// SingleRow emits exactly one empty row to drive the pipeline.
src := exec.NewSingleRowOperator()
// Unwind expands a fixed list; listFn ignores the (empty) input row.
unwind, err := exec.NewUnwind(src, func(exec.Row) (expr.ListValue, error) {
return expr.ListValue{
expr.IntegerValue(10),
expr.IntegerValue(20),
expr.IntegerValue(30),
}, nil
})
if err != nil {
fmt.Println("NewUnwind:", err)
return
}
// Limit passes at most two rows downstream.
limit, err := exec.NewLimit(unwind, 2)
if err != nil {
fmt.Println("NewLimit:", err)
return
}
// Drain runs the pipeline and always closes it before returning.
rows, err := exec.Drain(context.Background(), limit)
if err != nil {
fmt.Println("Drain:", err)
return
}
fmt.Println("rows:", len(rows))
for _, r := range rows {
fmt.Println("x =", r[0])
}
}
Output: rows: 2 x = 10 x = 20
type RowSlab ¶
type RowSlab struct {
// contains filtered or unexported fields
}
RowSlab is a bounded arena of pre-allocated rows. It eliminates per-row heap allocations by backing all rows in a single flat slice. Each call to [Alloc] hands out a sub-slice at zero GC cost after the initial backing allocation.
RowSlab is NOT safe for concurrent use; each pipeline stage owns its own instance, typically obtained from SlabPool.
Lifecycle ¶
- Obtain a slab from SlabPool.Get (or call NewRowSlab).
- Call [Alloc] for each row needed in the current batch.
- When the batch is fully processed, call [Reset] and return the slab to SlabPool.Put.
[Reset] zeroes the column values in every allocated row so that no expr.Value reference is retained past the batch boundary (preventing GC nepotism between batches).
func NewRowSlab ¶
NewRowSlab creates a RowSlab with the given column count and row capacity. width is the number of expr.Value slots pre-allocated per row; pass 0 for variable-width rows (callers supply their own slice to [AllocRaw]). capacity must be ≥ 1; DefaultSlabCapacity is a reasonable default.
func (*RowSlab) Alloc ¶
Alloc returns the next available pre-allocated row in the slab. It returns ErrSlabOverflow if the slab is exhausted. The row width matches the width passed to NewRowSlab; for variable-width slabs (width=0) use [AllocRaw].
func (*RowSlab) AllocRaw ¶
AllocRaw returns the next row slot for variable-width slabs (width=0). The caller is responsible for setting the returned row to a correctly-sized slice before use. For fixed-width slabs, use [Alloc].
type SemiApply ¶
type SemiApply struct {
// contains filtered or unexported fields
}
SemiApply emits each outer row for which the inner sub-plan produces at least one row. The inner plan is closed after the first match (short-circuit).
SemiApply is NOT safe for concurrent use.
func NewSemiApply ¶
NewSemiApply creates a SemiApply operator.
- outer is the driving (left) plan.
- inner is the correlated (right) sub-plan whose leaf is arg.
- arg is the Argument node seeded with each outer row before inner Init.
func (*SemiApply) Close ¶
Close closes the outer plan. The inner plan is already closed per-row inside Next; a redundant close here is safe because Close on a closed operator must be a no-op per the Operator contract.
func (*SemiApply) Next ¶
Next advances to the next outer row for which the inner plan has ≥1 result.
func (*SemiApply) PlanChildren ¶ added in v0.11.0
PlanChildren reports the outer input that drives this operator, then the inner sub-plan it re-drives for each outer row.
type SetAllProperties ¶
type SetAllProperties struct {
// contains filtered or unexported fields
}
SetAllProperties replaces or merges every property on an already-bound node or relationship per input row.
SetAllProperties is NOT safe for concurrent use.
func NewSetAllPropertiesFromEntity ¶
func NewSetAllPropertiesFromEntity( entityVar, sourceVar string, isReplace bool, schema map[string]int, child Operator, mutator GraphMutator, ) *SetAllProperties
NewSetAllPropertiesFromEntity creates a SetAllProperties operator copying every property from sourceVar (a bound node or relationship) to entityVar. isReplace selects `=` (true) vs `+=` (false) semantics.
func NewSetAllPropertiesFromExpr ¶ added in v0.9.0
func NewSetAllPropertiesFromExpr( entityVar string, isReplace bool, schema map[string]int, child Operator, mutator GraphMutator, ) *SetAllProperties
NewSetAllPropertiesFromExpr creates a SetAllProperties operator whose source is an arbitrary map-valued expression evaluated per row (installed via SetAllProperties.WithExprEvalFn). It backs the `SET n = <expr>` / `SET n += <expr>` forms whose right-hand side is neither a `{…}` literal, a bound entity variable, nor a `$param` — e.g. `SET n = properties(m)`, a map projection, or coalesce. The evaluated value is dispatched on its runtime kind at apply time (see [SetAllProperties.applyExprValue]).
func NewSetAllPropertiesFromMap ¶
func NewSetAllPropertiesFromMap( entityVar, mapLiteral string, isReplace bool, schema map[string]int, child Operator, mutator GraphMutator, ) (*SetAllProperties, error)
NewSetAllPropertiesFromMap creates a SetAllProperties operator writing every key/value pair from mapLiteral to entityVar. mapLiteral is the opaque literal-map string (e.g. `{a: 1, b: "x"}`) produced by the AST printer.
func NewSetAllPropertiesFromParam ¶
func NewSetAllPropertiesFromParam( entityVar, paramName string, isReplace bool, schema map[string]int, child Operator, mutator GraphMutator, ) *SetAllProperties
NewSetAllPropertiesFromParam creates a SetAllProperties operator writing every key/value pair from the named query parameter to entityVar. The parameter must resolve to a MapValue at exec time; non-map values are treated as a no-op.
func (*SetAllProperties) Close ¶
func (op *SetAllProperties) Close() error
Close closes the child operator.
func (*SetAllProperties) Init ¶
func (op *SetAllProperties) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*SetAllProperties) Next ¶
func (op *SetAllProperties) Next(out *Row) (bool, error)
Next pulls one row from the child and applies the configured whole-entity mutation. The row is forwarded unchanged so downstream operators (e.g. ProduceResults) can read the affected entity.
func (*SetAllProperties) PlanChildren ¶ added in v0.11.0
func (op *SetAllProperties) PlanChildren() []Operator
PlanChildren reports the input whose rows name the entities it overwrites.
func (*SetAllProperties) WithConstraints ¶
func (op *SetAllProperties) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *SetAllProperties
WithConstraints attaches a ConstraintRegistry and index.Manager for pre-write enforcement. Both must be non-nil. Returns op for chaining.
func (*SetAllProperties) WithExprEvalFn ¶ added in v0.9.0
func (op *SetAllProperties) WithExprEvalFn(fn ExprValueEvalFn) *SetAllProperties
WithExprEvalFn attaches a per-row evaluator for a whole map-valued RHS expression (see ExprValueEvalFn). The evaluated value is dispatched on its runtime kind at apply time: a map writes its entries (null-valued keys removed), a node/relationship copies its properties, a null clears the target for `=` and is a no-op for `+=`, and any other kind raises a TypeError. Pass nil to clear. Returns op for chaining.
func (*SetAllProperties) WithMapEvalFn ¶ added in v0.9.0
func (op *SetAllProperties) WithMapEvalFn(fn MapEvalFn) *SetAllProperties
WithMapEvalFn attaches a per-row evaluator for the source map when it holds a non-literal value (a variable reference, property access, or arithmetic — e.g. `SET n += {x: row.y}`). The evaluator's output supersedes the static parsedMap/nullKeys for each row, so the map's row-driven values are written (and null-valued keys removed) exactly as the literal map is. Without it the literal-only parser drops the non-literal entry and the target keeps null or stale data — the same fail-silent defect the CREATE/MERGE property evaluators exist to prevent. Pass nil to clear. Returns op for chaining.
func (*SetAllProperties) WithParams ¶
func (op *SetAllProperties) WithParams(params map[string]expr.Value) (*SetAllProperties, error)
WithParams attaches query parameters for $name substitution in the literal map and for parameter-sourced operators. Returns op for chaining.
func (*SetAllProperties) WithRelCols ¶
func (op *SetAllProperties) WithRelCols(rc RelCols) *SetAllProperties
WithRelCols marks entityVar as a relationship variable and records the row columns that hold the src and dst NodeIDs. Must be called before the first Next invocation. Returns op for chaining.
func (*SetAllProperties) WithSourceRelCols ¶
func (op *SetAllProperties) WithSourceRelCols(rc RelCols) *SetAllProperties
WithSourceRelCols marks sourceVar as a relationship variable and records the row columns that hold its src and dst NodeIDs. Must be called before the first Next invocation when SourceVar is a relationship. Returns op for chaining.
type SetLabels ¶
type SetLabels struct {
// contains filtered or unexported fields
}
SetLabels adds one or more labels to an already-bound node per input row.
Attaching a label puts the node under every UNIQUE constraint declared on that label, so the operator reserves the node's current value for each constrained property before it writes — see cypher/exec/label_constraints.go. Enforcement is inert unless a UNIQUE constraint is registered.
SetLabels is NOT safe for concurrent use.
func NewSetLabels ¶
func NewSetLabels( nodeVar string, labels []string, schema map[string]int, child Operator, mutator GraphMutator, ) *SetLabels
NewSetLabels creates a SetLabels operator.
func (*SetLabels) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input whose rows name the nodes it labels.
func (*SetLabels) WithConstraints ¶ added in v0.11.0
func (op *SetLabels) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *SetLabels
WithConstraints attaches the constraint registry and index manager so SetLabels enforces the UNIQUE constraints the added labels bring into play. Returns op for chaining.
type SetProperty ¶
type SetProperty struct {
// contains filtered or unexported fields
}
SetProperty sets or replaces properties on an already-bound node or relationship per input row. The entity is identified by entityVar. For nodes, the column value must be an IntegerValue-encoded NodeID or a NodeValue. For relationships, call WithRelCols to supply the endpoint column indices.
SetProperty is NOT safe for concurrent use.
func NewSetProperty ¶
func NewSetProperty( entityVar, propertyKey, valueExpr string, schema map[string]int, child Operator, mutator GraphMutator, ) (*SetProperty, error)
NewSetProperty creates a SetProperty operator.
entityVar is the variable name of the target node or relationship. propertyKey is the property key for single-property mode; pass empty for whole-entity mode. valueExpr is the opaque literal string from the IR. schema maps variable names to column indices. mutator is the graph write surface.
func (*SetProperty) Init ¶
func (op *SetProperty) Init(ctx context.Context) error
Init initialises the operator and its child.
func (*SetProperty) Next ¶
func (op *SetProperty) Next(out *Row) (bool, error)
Next pulls one row from the child and applies the property mutation.
func (*SetProperty) PlanChildren ¶ added in v0.11.0
func (op *SetProperty) PlanChildren() []Operator
PlanChildren reports the input whose rows name the entities it updates.
func (*SetProperty) WithConstraints ¶
func (op *SetProperty) WithConstraints(reg *ConstraintRegistry, mgr *index.Manager) *SetProperty
WithConstraints attaches a ConstraintRegistry and index.Manager for pre-write enforcement. Both must be non-nil. Returns op for chaining.
func (*SetProperty) WithParams ¶
func (op *SetProperty) WithParams(params map[string]expr.Value) *SetProperty
WithParams attaches query parameters for $name substitution in value expressions. Returns op for chaining.
func (*SetProperty) WithRelCols ¶
func (op *SetProperty) WithRelCols(rc RelCols) *SetProperty
WithRelCols marks entityVar as a relationship variable and records the row columns that hold the src and dst NodeIDs. Must be called before the first Next invocation. Returns op for chaining.
func (*SetProperty) WithValueEvalFn ¶
func (op *SetProperty) WithValueEvalFn(fn ValueEvalFn) *SetProperty
WithValueEvalFn attaches a per-row evaluator for the SET RHS expression. The closure is invoked whenever the operator needs to compute the new property value; it takes priority over the literal-string parser path for single-property assignments.
type ShortestPath ¶
type ShortestPath struct {
// contains filtered or unexported fields
}
ShortestPath is a Volcano pipeline operator that, for each input row, finds a single shortest path from srcCol to dstCol using BFS and emits one output row containing the flat alternating path list (or Null if unreachable).
ShortestPath is NOT safe for concurrent use.
func NewShortestPath ¶
func NewShortestPath(input Operator, src AdjacencySource, dir Direction, srcCol, dstCol int) *ShortestPath
NewShortestPath creates a ShortestPath operator.
- input is the upstream operator supplying (srcID, dstID) pairs.
- fwd is the forward CSR adjacency.
- rev is the reverse CSR (required for DirIn/DirBoth).
- dir is the traversal direction.
- srcCol / dstCol are the column indices in each input row for source and destination node IDs.
The returned operator has no type filter and no hop bounds; use ShortestPath.WithTypeFilter and ShortestPath.WithHopBounds to configure them.
func (*ShortestPath) Init ¶
func (op *ShortestPath) Init(ctx context.Context) error
Init initialises the operator.
func (*ShortestPath) Next ¶
func (op *ShortestPath) Next(out *Row) (bool, error)
Next emits one row per input row, containing the shortest path. When no path exists the row is emitted with a Null path (OPTIONAL MATCH) or dropped (MATCH) depending on the operator's optional flag.
func (*ShortestPath) PlanChildren ¶ added in v0.11.0
func (op *ShortestPath) PlanChildren() []Operator
PlanChildren reports the operator whose rows it searches paths from.
func (*ShortestPath) WithHopBounds ¶ added in v0.6.0
func (op *ShortestPath) WithHopBounds(minHops, maxHops int) *ShortestPath
WithHopBounds sets the accepted path-length window. minHops is the minimum length (a zero-length src==dst path is reported only when minHops == 0); maxHops caps the BFS depth ([shortestNoMaxHops] == 0 means unbounded). It returns op for chaining.
func (*ShortestPath) WithOptional ¶ added in v0.6.0
func (op *ShortestPath) WithOptional(optional bool) *ShortestPath
WithOptional selects the OPTIONAL MATCH no-path behaviour (emit a Null-path row) when optional is true; the default (false) drops the row when no path exists, the MATCH behaviour. It returns op for chaining.
func (*ShortestPath) WithPathPredicate ¶ added in v0.6.0
func (op *ShortestPath) WithPathPredicate(pred func(Row) (bool, error)) *ShortestPath
WithPathPredicate fuses a whole-path predicate onto the operator (#1786). The operator then returns the shortest path that SATISFIES pred (an exhaustive search), instead of the unconstrained shortest path. pred is called with the candidate's full output row. It returns op for chaining.
func (*ShortestPath) WithTypeFilter ¶ added in v0.6.0
func (op *ShortestPath) WithTypeFilter(edgeType string) *ShortestPath
WithTypeFilter restricts traversal to edges whose forward position is present in filter. edgeType is the non-empty "a filter was requested" gate (typically the pattern's first declared relationship type). It returns op for chaining.
Callers configure the operator before Init, as the planner does. Changing the filter afterwards nonetheless invalidates the reverse-position admit bitset derived from it, so this clears the once-only build flag rather than leaving a bitset that describes a filter no longer in force.
func (*ShortestPath) WithWorkBudget ¶ added in v0.7.0
func (op *ShortestPath) WithWorkBudget(maxPerRow, maxTotal int) *ShortestPath
WithWorkBudget overrides the exhaustive path-predicate search's per-input-row and aggregate per-query edge-traversal caps (see [ShortestPath.maxEdgesTraversed]). A non-positive value leaves the corresponding default in place. It returns op for chaining and is primarily a testing seam; production uses the defaults.
type SingleRow ¶
type SingleRow struct {
// contains filtered or unexported fields
}
SingleRow emits one empty row then signals exhaustion.
SingleRow is NOT safe for concurrent use.
func NewSingleRowOperator ¶
func NewSingleRowOperator() *SingleRow
NewSingleRowOperator returns a SingleRow operator.
type Skip ¶
type Skip struct {
// contains filtered or unexported fields
}
Skip is a Volcano pipeline operator that discards the first n rows from its child operator and then forwards all remaining rows.
Skip is NOT safe for concurrent use.
func NewSkip ¶
NewSkip creates a Skip operator that discards the first n rows from child. n must be ≥ 0; a skip of 0 is a no-op pass-through.
func (*Skip) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input whose leading rows it discards.
type SlabPool ¶
type SlabPool struct {
// contains filtered or unexported fields
}
SlabPool is a sync.Pool-backed pool of RowSlab instances with a fixed column width and capacity. Operators that process a high volume of rows should obtain slabs from a shared pool to reduce GC pressure.
SlabPool is safe for concurrent use.
func NewSlabPool ¶
NewSlabPool creates a SlabPool that vends RowSlabs with the given column width and row capacity.
type Sort ¶
type Sort struct {
// contains filtered or unexported fields
}
Sort is a blocking Volcano operator that collects all rows from its child, sorts them by the specified SortKey sequence, and emits them in order.
Sort is NOT safe for concurrent use.
func NewSort ¶
NewSort creates a Sort operator.
- child: the upstream operator to consume.
- keys: ORDER BY specification. Must not be empty.
- maxRows: upper bound on rows held in memory; pass 0 to use DefaultMaxSortRows.
func (*Sort) Init ¶
Init initialises the operator. The blocking collect+sort phase is deferred to the first Next call.
func (*Sort) Next ¶
Next emits the next sorted row. On the first call it collects and sorts all rows from the child (pipeline breaker). Subsequent calls step through the sorted slice.
func (*Sort) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input it orders.
func (*Sort) WithByteBudget ¶ added in v0.7.0
WithByteBudget bounds the estimated retained size of the buffered rows by maxBytes, using estimateRow for the per-row estimate. It complements the maxRows count cap so a few large-valued rows cannot exceed the engine's result-byte budget before the count cap fires (#1841). A non-positive maxBytes or nil estimateRow leaves the byte dimension disabled. Returns op for chaining and must be called before Init.
type SortKey ¶
type SortKey struct {
// Eval is an optional expression evaluator. When non-nil the sort key
// value is obtained by calling Eval(row) rather than reading row[ColIdx].
// This supports ORDER BY expressions that are not direct projection
// output columns (e.g. ORDER BY n.age after RETURN n).
Eval func(Row) (expr.Value, error)
// ColIdx is the zero-based index of the column within each Row.
// Ignored when Eval is non-nil.
ColIdx int
// Ascending controls the sort direction. true = ASC, false = DESC.
Ascending bool
}
SortKey describes a single ORDER BY column.
type StaticRows ¶ added in v0.9.0
type StaticRows struct {
// contains filtered or unexported fields
}
StaticRows emits a fixed slice of rows, one per Next call, in slice order.
StaticRows is NOT safe for concurrent use.
func NewStaticRows ¶ added in v0.9.0
func NewStaticRows(rows []Row) *StaticRows
NewStaticRows creates a StaticRows operator over rows. The slice is retained by reference and never modified; a nil or empty slice yields an operator that emits no rows.
func (*StaticRows) Close ¶ added in v0.9.0
func (op *StaticRows) Close() error
Close releases the row reference. It is idempotent and always returns nil.
type StorageRecordScan ¶ added in v0.11.0
type StorageRecordScan interface {
// contains filtered or unexported methods
}
StorageRecordScan marks an operator that READS RECORDS FROM STORAGE, one record per row it emits, so its logical storage-access count (its db-hits) equals its emitted row count (rmp #2238).
Why a marker rather than a counter ¶
Db-hits exist to distinguish a selective seek from a scan that filtered afterwards: both can emit the same handful of rows while touching wildly different amounts of storage. That distinction lives entirely in the LEAVES — which records were read — and every operator above them consumes rows its children already produced, touching no storage of its own.
For a leaf, "records read" and "rows emitted" are the same number by construction:
- a label or all-nodes scan yields one node record per emitted row;
- an index seek, seek-set or range scan yields one node record per posting-list entry it emits;
- an expand yields one relationship record per emitted neighbour.
So the count is available at the operator boundary, where the profiling wrapper already sits, and needs no counter threaded through any accessor. That is not a shortcut but the point: with nothing threaded, a non-PROFILE Run executes no counting CODE AT ALL — there is not even a nil check to skip on the hot path.
What this deliberately does not count ¶
PROPERTY READS. Neo4j charges a db-hit per property access, so its numbers for a filter-heavy plan are larger than GoGraph's. Counting them here would mean threading a counter into the property accessors — precisely the hot path the paragraph above protects — and would make every ordinary query pay for a diagnostic. The divergence is documented in docs/cypher.md rather than papered over with an estimate, because a db-hits figure that silently blends measured leaf reads with guessed property reads would be less useful than one whose meaning is exact.
An operator that does not implement this interface reports 0 db-hits, which is the honest answer for a pure row transformer.
type StringHashIndex ¶
type StringHashIndex struct {
// contains filtered or unexported fields
}
StringHashIndex adapts hash.Index[string] to the HashLookup interface. It accepts only expr.StringValue seek keys; other kinds return ErrIndexTypeMismatch.
func NewStringHashIndex ¶
func NewStringHashIndex(idx interface {
LookupAppend(value string, dst []uint64) []uint64
}) *StringHashIndex
NewStringHashIndex constructs a StringHashIndex.
func (*StringHashIndex) LookupAppend ¶ added in v0.6.0
LookupAppend implements HashLookup.
type StringRangeIndex ¶
type StringRangeIndex struct {
// contains filtered or unexported fields
}
StringRangeIndex adapts btree.Index[string] to the [rangeLookup] interface. An unbounded lower bound is "" (the true minimum of the string order); an unbounded (or non-string) upper bound routes to the index's open-ended RangeFrom scan rather than a fixed sentinel — no fixed key is a true maximum for a variable-length string, so a sentinel cap would silently drop any key sorting above it (#F-CY1).
func NewStringRangeIndex ¶
func NewStringRangeIndex(idx interface {
Range(lo, hi string) *roaring64.Bitmap
RangeFrom(lo string) *roaring64.Bitmap
}) *StringRangeIndex
NewStringRangeIndex constructs a StringRangeIndex.
func (*StringRangeIndex) RangeBitmap ¶
func (r *StringRangeIndex) RangeBitmap(lo, hi expr.Value) *roaring64.Bitmap
RangeBitmap implements [rangeLookup]. An unbounded-above range (nil/NULL or a non-string upper bound) scans open-ended via RangeFrom so the bitmap is a genuine superset of every key >= lo (#F-CY1); a bounded upper uses the inclusive [lo, hi] Range.
type SubplanFactory ¶ added in v0.6.0
SubplanFactory builds an independent physical sub-plan that scans exactly the NodeIDs in morsel and applies the fused Filter/Projection over them. Each call must return a fresh operator tree that shares NO mutable state with any other call's tree (the planner achieves this with a per-worker schema map and a per-worker buildOpts copy whose lazily-written fields are zeroed). The morsel slice is owned by the caller (ParallelScanProject) and is read-only for the lifetime of the returned operator; the factory must not retain or mutate it beyond feeding it to the morsel scan leaf.
The returned operator is driven Init → Next* → Close by exactly one worker goroutine. It must honour the standard Operator lifecycle.
type TargetEvalFn ¶
TargetEvalFn evaluates a DELETE / DETACH DELETE target expression against the current input row and returns the resolved value. The exec operator inspects the value: NodeValue / IntegerValue selects the node by ID; RelationshipValue selects the relationship; null is a row-passthrough no-op (matches openCypher 9 §3.5.8).
type Top ¶
type Top struct {
// contains filtered or unexported fields
}
Top is a blocking Volcano operator that emits the N smallest rows (per the given sort keys) from its child, using a bounded heap for O(M log N) memory and time.
Top is NOT safe for concurrent use.
func NewTop ¶
NewTop creates a Top operator.
- child: the upstream operator to consume.
- keys: ORDER BY specification. Must not be empty.
- n: number of rows to return. Must be ≥ 0; n == 0 yields an empty result while still draining the child (ORDER BY … LIMIT 0, see #1801).
func (*Top) Init ¶
Init initialises the operator. The blocking consume phase is deferred to the first Next call.
func (*Top) Next ¶
Next emits the next top-N row in sorted order. On the first call it consumes all rows from the child and finalises the heap.
func (*Top) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input it orders and truncates.
type Union ¶
type Union struct {
// contains filtered or unexported fields
}
Union emits the set-union of left and right: all rows from both sides with duplicates removed. It is implemented as UnionAll wrapped in a Distinct operator.
Schema mismatch detection is inherited from UnionAll.
Union is NOT safe for concurrent use.
func NewUnion ¶
NewUnion creates a Union operator that deduplicates the concatenation of left and right.
- maxDistinct: upper bound on distinct rows; pass 0 to use DefaultMaxDistinct.
func (*Union) PlanChildren ¶ added in v0.11.0
PlanChildren reports the Distinct it wraps.
Union is a Distinct over a UnionAll; reporting the Distinct keeps the rendered tree faithful to what actually executes rather than flattening two operators into one.
type UnionAll ¶
type UnionAll struct {
// contains filtered or unexported fields
}
UnionAll is a Volcano operator that concatenates the output of left and right children without deduplication. It validates that both sides produce rows of the same width (column count).
UnionAll is NOT safe for concurrent use.
func NewUnionAll ¶
NewUnionAll creates a UnionAll operator that concatenates left then right.
func (*UnionAll) Close ¶
Close closes both child operators. Both are always attempted; if both fail the errors are joined.
func (*UnionAll) Next ¶
Next emits rows from left until exhausted, then emits rows from right. Returns ErrSchemaMismatch if the first right-side row has a different column count than the first left-side row.
func (*UnionAll) PlanChildren ¶ added in v0.11.0
PlanChildren reports the two inputs whose rows it concatenates.
type Unwind ¶
type Unwind struct {
// contains filtered or unexported fields
}
Unwind is a Volcano pipeline operator that implements the UNWIND clause. For each input row it evaluates a list expression and emits one output row per list element, appending the element value as a new column.
Unwind is NOT safe for concurrent use.
func NewUnwind ¶
func NewUnwind(child Operator, listFn UnwindListFn) (*Unwind, error)
NewUnwind creates an Unwind operator.
child provides the context rows. listFn is evaluated once per input row and must return the list to expand. The caller is responsible for appending the element column to the output row; Unwind handles that internally.
Both child and listFn are required: a nil argument returns the typed sentinel ErrUnwindNilChild or ErrUnwindNilListFn respectively, so callers can distinguish the cause via errors.Is. NewUnwind never panics.
func (*Unwind) Close ¶
Close releases resources and closes the child operator.
Close is idempotent within a single pipeline lifecycle: calling it more than once between two Init invocations returns nil from the second and later calls and does NOT propagate to op.child.Close again. The idempotency guard is reset by Init, so an Init→Close→Init→Close sequence still closes the child twice — once per cycle, as expected.
func (*Unwind) Init ¶
Init initialises the operator and its child. It clears all per-iteration state (curRow, curList, listIdx) and resets the idempotency guard (closed) so that Init is the exact dual of Close, allowing an operator instance to be safely re-Init'd after a previous Close.
func (*Unwind) Next ¶
Next advances to the next element. It pulls a new input row from the child whenever the current list is exhausted, then emits one row per element.
Returns (true, nil) when an output row was written to out, (false, nil) at end-of-stream, (false, err) on error.
func (*Unwind) PlanChildren ¶ added in v0.11.0
PlanChildren reports the input whose list column it expands.
type UnwindListFn ¶
UnwindListFn evaluates the list expression for one input row. It returns a expr.ListValue when the expression evaluates to a list, or nil/empty when there is nothing to expand.
type ValueEvalFn ¶
ValueEvalFn evaluates a SET RHS expression against the current input row and returns the resulting property value plus a flag distinguishing the "no value produced" case from the "explicit null" case. The exec operator uses null/no-value semantics to either delete (null) or no-op (no value).
type VarLengthConfig ¶
type VarLengthConfig struct {
// EdgeType, when non-empty, restricts expansion to edges of this type.
EdgeType string
// ExcludedRelCols lists column indices in the input row holding edge
// identifiers (IntegerValue or RelationshipValue) that must not be
// traversed inside this VLE step. Implements the openCypher
// no-repeated-relationships rule across distinct rel patterns within
// the same MATCH (e.g. `MATCH ()-[r:EDGE]-() MATCH (n)-[*0..1]-()-[r]
// -()-[*0..1]-(m)` — the two variable-length steps must not reuse the
// edge bound to `r`). The visited bitset is pre-populated with each
// listed column's edge position at BFS seed time.
ExcludedRelCols []int
// InputCol is the column index in each input row that holds the source
// NodeID. Defaults to 0.
InputCol int
// MinHops is the minimum path length (inclusive). Must be ≥ 0.
MinHops int
// MaxHops is the maximum path length (inclusive). Must be ≥ MinHops.
// Use math.MaxInt for unbounded (not recommended without a safety cap).
MaxHops int
// MaxEdgesTraversed is the safety cap on edge traversals per input row.
// Defaults to [defaultMaxEdgesTraversed] (1,000,000) when 0.
MaxEdgesTraversed int
// MaxTotalEdgesTraversed is the aggregate safety cap on edge traversals
// across all input rows for the whole query — it is NOT reset per row, so it
// bounds the M × (per-row cost) multiplication that the per-row cap alone
// cannot (#1478). Defaults to [defaultMaxTotalEdgesTraversed] (100,000,000)
// when 0.
MaxTotalEdgesTraversed int
// Direction to follow. Defaults to DirOut when zero.
Direction Direction
}
VarLengthConfig carries configuration for NewVarLengthExpand.
type VarLengthExpand ¶
type VarLengthExpand struct {
// contains filtered or unexported fields
}
VarLengthExpand is a Volcano pipeline operator that performs bounded BFS variable-length expansion.
VarLengthExpand is NOT safe for concurrent use.
func NewVarLengthExpand ¶
func NewVarLengthExpand(input Operator, src AdjacencySource, cfg *VarLengthConfig) *VarLengthExpand
NewVarLengthExpand creates a VarLengthExpand operator. cfg is read-only and taken by pointer to avoid copying the configuration struct on this hot path.
func (*VarLengthExpand) Init ¶
func (op *VarLengthExpand) Init(ctx context.Context) error
Init initialises the operator.
func (*VarLengthExpand) Next ¶
func (op *VarLengthExpand) Next(out *Row) (bool, error)
Next emits the next (inputRow... || pathEdgesAsListValue || dstNodeID) row. The path is encoded as a expr.ListValue of edge positions (IntegerValues), followed by the destination node ID as an IntegerValue.
func (*VarLengthExpand) PlanChildren ¶ added in v0.11.0
func (op *VarLengthExpand) PlanChildren() []Operator
PlanChildren reports the operator whose rows it expands from.
Source Files
¶
- agg_column_kernel.go
- all_nodes_count_scan.go
- apply.go
- argument.go
- byte_budget.go
- chunk.go
- columnar_filter.go
- columnar_limit.go
- columnar_project.go
- constraint_journal.go
- constraints.go
- correlated_apply.go
- count_writeback.go
- create_constraint.go
- create_index.go
- create_node.go
- create_relationship.go
- csrprobe.go
- cyphermorphism.go
- delete.go
- detach_delete.go
- distinct.go
- driver.go
- drop_constraint.go
- drop_index.go
- eager.go
- eager_aggregation.go
- expand.go
- expand_intersect.go
- filter.go
- foreach.go
- global_aggregate_adapter.go
- hash_join.go
- hash_join_columnar.go
- index_writeback.go
- join_index_nested_loop.go
- label_constraints.go
- label_count_scan.go
- limit.go
- merge.go
- merge_pattern.go
- merge_relationship.go
- merge_search.go
- merge_setall.go
- operator.go
- optional_expand.go
- parallel.go
- parallel_aggregate.go
- parallel_aggregate_scan.go
- parallel_governor.go
- parallel_scan_project.go
- passthrough.go
- plan.go
- plan_children.go
- plan_detail.go
- procedure_call.go
- produce_results.go
- profile.go
- project.go
- query_counters.go
- remove.go
- revtofwd.go
- rollup_apply.go
- row.go
- rowcount_hint.go
- scan_all.go
- scan_index_btree.go
- scan_index_hash.go
- scan_index_hash_set.go
- scan_label.go
- semi_apply.go
- set.go
- set_all.go
- shortest_path.go
- shortest_path_bidir.go
- single_row.go
- sort.go
- static_rows.go
- temporal_literal.go
- top.go
- union.go
- unwind.go
- varlen_expand.go
- write_graph.go