This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git
The following commit(s) were added to refs/heads/main by this push:
new 772af76b2 perf(variant): avoid copying unescaped path members (#2242)
772af76b2 is described below
commit 772af76b29cb50c546046f728884ba69490cee5d
Author: Minh Vu <[email protected]>
AuthorDate: Wed Oct 21 22:43:27 2026 +0200
perf(variant): avoid copying unescaped path members (#2242)
* perf(variant): avoid copying unescaped path members
Signed-off-by: Minh Vu <[email protected]>
* perf(variant): avoid retaining path for empty selectors
* style(variant): satisfy nlreturn for quoted member parsing
---------
Signed-off-by: Minh Vu <[email protected]>
---
variant_path.go | 20 +++++++++--
variant_path_bench_test.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++
variant_path_test.go | 5 +++
3 files changed, 108 insertions(+), 3 deletions(-)
diff --git a/variant_path.go b/variant_path.go
index 34080932e..307ddd347 100644
--- a/variant_path.go
+++ b/variant_path.go
@@ -127,20 +127,33 @@ func parseBracketSelector(s, path string) (string, int,
error) {
return "", 0, fmt.Errorf("%w: unsupported variant path %q (only
quoted member selectors like ['name'] are supported)", ErrInvalidArgument, path)
}
+ // Delay copying until an escape is found. Plain quoted members can
+ // reference the original path without allocating a new string.
var b strings.Builder
+ start := 2
for i := 2; i < len(s); {
- c := s[i]
- switch c {
+ switch s[i] {
case '\'':
if i+1 >= len(s) || s[i+1] != ']' {
return "", 0, fmt.Errorf("%w: invalid variant
path %q (expected ']' after quoted member)", ErrInvalidArgument, path)
}
+ if start == 2 {
+ if i == 2 {
+ // Avoid retaining the input path for
an empty member.
+ return "", i + 2, nil
+ }
+
+ return s[2:i], i + 2, nil
+ }
+ b.WriteString(s[start:i])
+
return b.String(), i + 2, nil
case '\\':
if i+1 >= len(s) {
return "", 0, fmt.Errorf("%w: invalid variant
path %q (dangling escape)", ErrInvalidArgument, path)
}
+ b.WriteString(s[start:i])
switch e := s[i+1]; e {
case 'b':
b.WriteByte('\b')
@@ -161,14 +174,15 @@ func parseBracketSelector(s, path string) (string, int,
error) {
}
b.WriteRune(r)
i += consumed
+ start = i
continue
default:
return "", 0, fmt.Errorf("%w: invalid variant
path %q (unknown escape \\%c)", ErrInvalidArgument, path, e)
}
i += 2
+ start = i
default:
- b.WriteByte(c)
i++
}
}
diff --git a/variant_path_bench_test.go b/variant_path_bench_test.go
new file mode 100644
index 000000000..4cbb4d229
--- /dev/null
+++ b/variant_path_bench_test.go
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package iceberg
+
+import "testing"
+
+var (
+ benchmarkVariantPathMember string
+ benchmarkVariantPathConsumed int
+ benchmarkVariantPathFields []string
+)
+
+// BenchmarkParseBracketSelector measures the selector fast path separately.
+func BenchmarkParseBracketSelector(b *testing.B) {
+ for _, tt := range []struct {
+ name, selector string
+ }{
+ {"Short", "['id']"},
+ {"Medium", "['event_type']"},
+ {"Long",
"['this_is_a_very_long_field_name_for_analytic_queries']"},
+ {"UTF8", "['naïve_東京_データ']"},
+ {"Escaped", "['o\\'brien']"},
+ {"UnicodeEscape", "['\\uD840\\uDC00']"},
+ } {
+ b.Run(tt.name, func(b *testing.B) {
+ path := "$" + tt.selector
+ b.ReportAllocs()
+
+ var member string
+ var consumed int
+ var err error
+ for range b.N {
+ member, consumed, err =
parseBracketSelector(tt.selector, path)
+ }
+ if err != nil {
+ b.Fatal(err)
+ }
+ benchmarkVariantPathMember = member
+ benchmarkVariantPathConsumed = consumed
+ })
+ }
+}
+
+// BenchmarkParseVariantPath measures the complete parser, including slice
allocations.
+func BenchmarkParseVariantPath(b *testing.B) {
+ for _, tt := range []struct {
+ name, path string
+ }{
+ {"One", "$['event_type']"},
+ {"Two", "$['location']['latitude']"},
+ {"Four", "$['customer']['account']['profile']['event_type']"},
+ {"UTF8", "$['naïve_東京_データ']"},
+ {"Escaped", "$['o\\'brien']"},
+ {"Mixed", "$['customer'].profile['created_at']"},
+ {"DotOnly", "$.customer.profile.created_at"},
+ } {
+ b.Run(tt.name, func(b *testing.B) {
+ b.ReportAllocs()
+
+ var fields []string
+ var err error
+ for range b.N {
+ fields, err = parseVariantPath(tt.path)
+ }
+ if err != nil {
+ b.Fatal(err)
+ }
+ benchmarkVariantPathFields = fields
+ })
+ }
+}
diff --git a/variant_path_test.go b/variant_path_test.go
index a9a113e23..2675e540c 100644
--- a/variant_path_test.go
+++ b/variant_path_test.go
@@ -57,6 +57,11 @@ func TestParseVariantPath(t *testing.T) {
{"underscore and digits", "$._a1.b2", []string{"_a1", "b2"}},
{"non-ascii first char", "$.naïve", []string{"naïve"}},
{"bracket notation", "$['event_id']", []string{"event_id"}},
+ {"bracket empty quoted member", "$['']", []string{""}},
+ {"bracket plain unicode", "$['naïve_東京']",
[]string{"naïve_東京"}},
+ {"bracket literal delimiters", "$['a.b[0]']",
[]string{"a.b[0]"}},
+ {"bracket two different escapes", "$['a\\nb\\tc']",
[]string{"a\nb\tc"}},
+ {"bracket mixed escaped and plain runs",
"$['pre\\'mid\\\\end']", []string{"pre'mid\\end"}},
{"bracket nested", "$['location']['latitude']",
[]string{"location", "latitude"}},
{"bracket dotted name", "$['user.name']",
[]string{"user.name"}},
{"bracket leading digit", "$['1abc']", []string{"1abc"}},