From 4262621c85460c1647f37d89e520187fb8062cc7 Mon Sep 17 00:00:00 2001 From: Noah Treuhaft Date: Mon, 1 Jun 2026 13:07:02 -0400 Subject: [PATCH] vector: eliminate nils from vector.Dynamic returned by Apply vector.Apply can return a Dynamic in which Dynamic.Values contains nils. It does this when the rip function's Dynamic parameter contains zero-length vectors, which is common with Unions. Since these nils can cause panics elsewhere, eliminate them in the stitch function. --- vector/apply.go | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/vector/apply.go b/vector/apply.go index 0e41a87c2..4d0a0313c 100644 --- a/vector/apply.go +++ b/vector/apply.go @@ -42,6 +42,7 @@ func Apply(opt ApplyOpt, eval func(...Any) Any, vecs ...Any) Any { results[i] = Apply(opt, eval, ripped...) } } + // stitch removes nils and replaces Dynamics with their values. return stitch(d.Tags, results) } @@ -74,33 +75,40 @@ func rip(vecs []Any, d *Dynamic) iter.Seq2[int, []Any] { } } -// stitch returns a Dynamic for tags and vecs. If vecs contains any Dynamics, -// stitch flattens them and returns a value containing no nested Dynamics. +// stitch returns a Dynamic for tags and vecs with nil entries removed and +// Dynamic entries replaced by their values (i.e., it flattens one level of +// Dynamic). func stitch(tags []uint32, vecs []Any) Any { - var foundDynamic bool + var needStitch bool var newVecsLen int for _, vec := range vecs { - if d, ok := vec.(*Dynamic); ok { - foundDynamic = true - newVecsLen += len(d.Values) - } else { + switch vec := vec.(type) { + case nil: + needStitch = true + case *Dynamic: + needStitch = true + newVecsLen += len(vec.Values) + default: newVecsLen++ } } - if !foundDynamic { + if !needStitch { return NewDynamic(tags, vecs) } - newVecs := make([]Any, 0, newVecsLen) // vecs but with nested Dynamics replaced by their values + newVecs := make([]Any, 0, newVecsLen) // vecs but without nils and with Dynamics replaced by their values nestedTags := make([][]uint32, len(vecs)) // tags from nested Dynamics (nil for non-Dynamics) shifts := make([]uint32, len(vecs)) // tag + shift[tag] translates tag to newVecs var lastShift uint32 for i, vec := range vecs { shifts[i] = lastShift - if d, ok := vec.(*Dynamic); ok { - newVecs = append(newVecs, d.Values...) - nestedTags[i] = d.Tags - lastShift += uint32(len(d.Values)) - 1 - } else { + switch vec := vec.(type) { + case nil: + lastShift-- + case *Dynamic: + newVecs = append(newVecs, vec.Values...) + nestedTags[i] = vec.Tags + lastShift += uint32(len(vec.Values)) - 1 + default: newVecs = append(newVecs, vec) } }