This is an automated email from the ASF dual-hosted git repository.

laskoviymishka 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 a58fdf414 perf(table): limit parquet row-group stats to filter columns 
(#2156)
a58fdf414 is described below

commit a58fdf414476a10686ca731e9fa383d74a68a932
Author: Minh Vu <[email protected]>
AuthorDate: Fri Oct 9 19:30:52 2026 +0200

    perf(table): limit parquet row-group stats to filter columns (#2156)
    
    
    Signed-off-by: Minh Vu <[email protected]>
---
 table/arrow_filter_plan.go                         |   6 +
 table/arrow_filter_plan_stats_test.go              |  60 ++++++
 table/arrow_scanner.go                             |  15 +-
 table/arrow_scanner_pruning_internal_test.go       |   4 +
 table/internal/parquet_files.go                    |  20 +-
 table/internal/parquet_stats_projection_test.go    | 212 +++++++++++++++++++++
 table/row_group_stats_filter_columns_bench_test.go | 101 ++++++++++
 7 files changed, 413 insertions(+), 5 deletions(-)

diff --git a/table/arrow_filter_plan.go b/table/arrow_filter_plan.go
index 0c4227c82..7d693663b 100644
--- a/table/arrow_filter_plan.go
+++ b/table/arrow_filter_plan.go
@@ -40,6 +40,7 @@ import (
 // per-row-group maps.
 type compiledFileFilterPlan struct {
        statsFilter     iceberg.BooleanExpression
+       statsFieldIDs   []int
        bloomPreds      []tblutils.RowGroupBloomPred
        dictionaryPreds []tblutils.RowGroupDictionaryPred
 
@@ -370,8 +371,13 @@ func compileFileFilterPlan(
                if err != nil {
                        return nil, err
                }
+               statsFieldIDs, err := iceberg.ExtractFieldIDs(statsFilter)
+               if err != nil {
+                       return nil, err
+               }
 
                plan.statsFilter = statsFilter
+               plan.statsFieldIDs = statsFieldIDs
                plan.bloomPreds = bloomPreds
                plan.dictionaryPreds = dictionaryPreds
        }
diff --git a/table/arrow_filter_plan_stats_test.go 
b/table/arrow_filter_plan_stats_test.go
new file mode 100644
index 000000000..4be33343c
--- /dev/null
+++ b/table/arrow_filter_plan_stats_test.go
@@ -0,0 +1,60 @@
+// 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 table
+
+import (
+       "testing"
+
+       "github.com/apache/iceberg-go"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestCompiledFileFilterPlanTracksStatsFieldIDs(t *testing.T) {
+       schema := iceberg.NewSchema(1,
+               iceberg.NestedField{ID: 1, Name: "id", Type: 
iceberg.PrimitiveTypes.Int64, Required: true},
+               iceberg.NestedField{ID: 2, Name: "value", Type: 
iceberg.PrimitiveTypes.Int64, Required: true},
+               iceberg.NestedField{ID: 3, Name: "payload", Type: 
iceberg.PrimitiveTypes.String},
+       )
+       filter, err := iceberg.BindExpr(schema, iceberg.NewAnd(
+               iceberg.EqualTo(iceberg.Reference("id"), int64(7)),
+               iceberg.GreaterThan(iceberg.Reference("value"), int64(10)),
+       ), true)
+       require.NoError(t, err)
+
+       plan, err := compileFileFilterPlan(schema, filter, true, false, true)
+       require.NoError(t, err)
+
+       assert.ElementsMatch(t, []int{1, 2}, plan.statsFieldIDs)
+}
+
+func TestCompiledFileFilterPlanKeepsEmptyStatsFieldIDsForAlwaysFalse(t 
*testing.T) {
+       schema := iceberg.NewSchema(1,
+               iceberg.NestedField{ID: 1, Name: "id", Type: 
iceberg.PrimitiveTypes.Int64, Required: true},
+       )
+       plan, err := compileFileFilterPlan(schema, iceberg.AlwaysFalse{}, true, 
false, true)
+       require.NoError(t, err)
+       require.NotNil(t, plan.statsFieldIDs)
+       assert.Empty(t, plan.statsFieldIDs)
+
+       eval := plan.statsEvaluator()
+       require.NotNil(t, eval)
+       keep, err := eval(buildRowGroupMetricsMetadata(t, 1, 1, 
true).RowGroup(0), nil)
+       require.NoError(t, err)
+       assert.False(t, keep)
+}
diff --git a/table/arrow_scanner.go b/table/arrow_scanner.go
index c7fe27198..c1ebe9fd4 100644
--- a/table/arrow_scanner.go
+++ b/table/arrow_scanner.go
@@ -881,6 +881,10 @@ func newPositionDeleteRowGroupTester(schema *arrow.Schema, 
targets map[string]st
        if err != nil {
                return nil, err
        }
+       statsFieldIDs, err := iceberg.ExtractFieldIDs(filter)
+       if err != nil {
+               return nil, err
+       }
 
        statsFn, err := 
newParquetRowGroupStatsEvaluator(iceberg.PositionalDeleteSchema, filter, false)
        if err != nil {
@@ -892,8 +896,9 @@ func newPositionDeleteRowGroupTester(schema *arrow.Schema, 
targets map[string]st
        }
 
        return &tblutils.ParquetRowGroupTester{
-               StatsFn:    statsFn,
-               BloomPreds: bloomPreds,
+               StatsFn:       statsFn,
+               StatsFieldIDs: statsFieldIDs,
+               BloomPreds:    bloomPreds,
        }, nil
 }
 
@@ -1968,6 +1973,7 @@ func (as *arrowScan) processRecordsWithPlans(
                if plans != nil && plans.pruning != nil {
                        tester = &tblutils.ParquetRowGroupTester{
                                StatsFn:         plans.pruning.statsEvaluator(),
+                               StatsFieldIDs:   plans.pruning.statsFieldIDs,
                                BloomPreds:      plans.pruning.bloomPreds,
                                DictionaryPreds: plans.pruning.dictionaryPreds,
                        }
@@ -2004,6 +2010,10 @@ func (as *arrowScan) processRecordsWithPlans(
                        if err != nil {
                                return err
                        }
+                       statsFieldIDs, err := 
iceberg.ExtractFieldIDs(filePruningFilter)
+                       if err != nil {
+                               return err
+                       }
 
                        statsFn, err := 
newParquetRowGroupStatsEvaluator(fileSchema, filePruningFilter, false)
                        if err != nil {
@@ -2022,6 +2032,7 @@ func (as *arrowScan) processRecordsWithPlans(
 
                        tester = &tblutils.ParquetRowGroupTester{
                                StatsFn:         statsFn,
+                               StatsFieldIDs:   statsFieldIDs,
                                BloomPreds:      bloomPreds,
                                DictionaryPreds: dictionaryPreds,
                        }
diff --git a/table/arrow_scanner_pruning_internal_test.go 
b/table/arrow_scanner_pruning_internal_test.go
index 4af6adc80..007e3d0d2 100644
--- a/table/arrow_scanner_pruning_internal_test.go
+++ b/table/arrow_scanner_pruning_internal_test.go
@@ -118,6 +118,7 @@ func TestProcessRecordsUsesRowGroupFilterForPruning(t 
*testing.T) {
        require.NoError(t, err)
 
        require.NotNil(t, reader.tester)
+       assert.ElementsMatch(t, []int{1}, reader.tester.StatsFieldIDs)
        require.Len(t, reader.tester.BloomPreds, 1)
        assert.Equal(t, 1, reader.tester.BloomPreds[0].FieldID)
        assert.Len(t, reader.tester.BloomPreds[0].PhysBytes, 1)
@@ -183,6 +184,7 @@ func 
TestProcessRecordsRebindsRowGroupFilterToPromotedFileSchema(t *testing.T) {
        require.NoError(t, err)
 
        assert.True(t, reader.statsResult, "matching INT32 row-group stats 
should be retained")
+       assert.Equal(t, []int{1}, reader.tester.StatsFieldIDs)
        require.Len(t, reader.tester.BloomPreds, 1)
        assert.Equal(t, []byte{1, 0, 0, 0}, 
reader.tester.BloomPreds[0].PhysBytes[0],
                "the bloom predicate should use the INT32 file encoding")
@@ -231,6 +233,8 @@ func TestProcessRecordsDoesNotPruneMissingInitialDefault(t 
*testing.T) {
        require.NoError(t, err)
 
        require.NotNil(t, reader.tester)
+       require.NotNil(t, reader.tester.StatsFieldIDs)
+       assert.Empty(t, reader.tester.StatsFieldIDs)
        assert.Empty(t, reader.tester.BloomPreds,
                "a missing field with an initial-default must disable bloom 
pruning")
        assert.Empty(t, reader.tester.DictionaryPreds,
diff --git a/table/internal/parquet_files.go b/table/internal/parquet_files.go
index 76e4d04f4..7a8b36f37 100644
--- a/table/internal/parquet_files.go
+++ b/table/internal/parquet_files.go
@@ -1914,7 +1914,11 @@ type RowGroupDictionaryPred struct {
 // group pruning.
 // Pass it as the tester argument to wrapPqArrowReader.GetRecords.
 type ParquetRowGroupTester struct {
-       StatsFn         func(*metadata.RowGroupMetaData, []int) (bool, error)
+       StatsFn func(*metadata.RowGroupMetaData, []int) (bool, error)
+       // StatsFieldIDs limits StatsFn to Parquet columns referenced by the 
pruning
+       // expression. Nil preserves the historical behavior of using all read 
columns.
+       // A non-nil empty slice requests no column statistics.
+       StatsFieldIDs   []int
        BloomPreds      []RowGroupBloomPred      // nil = no bloom filter pass
        DictionaryPreds []RowGroupDictionaryPred // nil = no dictionary filter 
pass
        // RangeSet indicates that Start and Length came from an explicit scan 
task.
@@ -2064,9 +2068,19 @@ func (w wrapPqArrowReader) GetRecords(ctx 
context.Context, cols []int, tester an
                        dictionaryReader *file.Reader
                )
 
-               if len(rowGroupTester.BloomPreds) > 0 || 
len(rowGroupTester.DictionaryPreds) > 0 {
+               if (rowGroupTester.StatsFn != nil && 
len(rowGroupTester.StatsFieldIDs) > 0) ||
+                       len(rowGroupTester.BloomPreds) > 0 || 
len(rowGroupTester.DictionaryPreds) > 0 {
                        fieldIDToColIdx = buildFieldIDToColIdx(fileMeta)
                }
+               statsCols := cols
+               if rowGroupTester.StatsFn != nil && 
rowGroupTester.StatsFieldIDs != nil {
+                       statsCols = make([]int, 0, 
len(rowGroupTester.StatsFieldIDs))
+                       for _, fieldID := range rowGroupTester.StatsFieldIDs {
+                               if colIdx, ok := fieldIDToColIdx[fieldID]; ok {
+                                       statsCols = append(statsCols, colIdx)
+                               }
+                       }
+               }
                var dictionaryPredsByColumn map[int][]int
                if len(rowGroupTester.DictionaryPreds) > 0 {
                        dictionaryPredsByColumn = 
groupRowGroupDictionaryPredicates(
@@ -2130,7 +2144,7 @@ func (w wrapPqArrowReader) GetRecords(ctx 
context.Context, cols []int, tester an
                        }
                        if use && rowGroupTester.StatsFn != nil {
                                var err error
-                               use, err = rowGroupTester.StatsFn(rgMeta, cols)
+                               use, err = rowGroupTester.StatsFn(rgMeta, 
statsCols)
                                if err != nil {
                                        return nil, err
                                }
diff --git a/table/internal/parquet_stats_projection_test.go 
b/table/internal/parquet_stats_projection_test.go
new file mode 100644
index 000000000..222c9f608
--- /dev/null
+++ b/table/internal/parquet_stats_projection_test.go
@@ -0,0 +1,212 @@
+// 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 internal_test
+
+import (
+       "bytes"
+       "context"
+       "testing"
+
+       "github.com/apache/arrow-go/v18/arrow/array"
+       "github.com/apache/arrow-go/v18/parquet"
+       "github.com/apache/arrow-go/v18/parquet/file"
+       "github.com/apache/arrow-go/v18/parquet/metadata"
+       "github.com/apache/arrow-go/v18/parquet/schema"
+       "github.com/apache/iceberg-go/table/internal"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+func TestParquetRowGroupStatsUseRequestedFieldIDs(t *testing.T) {
+       data := buildMultiColumnDictionaryTestParquet(t, 
multiColumnDictionaryTestRowGroup{
+               ids:        []int32{1},
+               categories: []string{"a"},
+       })
+
+       for _, test := range []struct {
+               name     string
+               fieldIDs []int
+               wantCols []int
+       }{
+               {name: "nil uses read columns", wantCols: []int{0, 1}},
+               {name: "filter column only", fieldIDs: []int{2}, wantCols: 
[]int{1}},
+               {name: "no filter columns", fieldIDs: []int{}, wantCols: 
[]int{}},
+               {name: "missing field", fieldIDs: []int{999}, wantCols: 
[]int{}},
+       } {
+               t.Run(test.name, func(t *testing.T) {
+                       rdr := openBloomTestReader(t, data)
+                       defer rdr.Close()
+
+                       var calls [][]int
+                       tester := &internal.ParquetRowGroupTester{
+                               StatsFn: func(_ *metadata.RowGroupMetaData, 
cols []int) (bool, error) {
+                                       calls = append(calls, append([]int{}, 
cols...))
+
+                                       return true, nil
+                               },
+                               StatsFieldIDs: test.fieldIDs,
+                       }
+                       rr, err := rdr.GetRecords(context.Background(), 
[]int{0, 1}, tester)
+                       require.NoError(t, err)
+                       assert.Equal(t, int64(1), countRecords(t, rr))
+                       require.Len(t, calls, 1)
+                       assert.Equal(t, test.wantCols, calls[0])
+               })
+       }
+}
+
+func TestParquetRowGroupStatsPruneUsingUnprojectedColumn(t *testing.T) {
+       data := buildMultiColumnDictionaryTestParquet(t,
+               multiColumnDictionaryTestRowGroup{ids: []int32{9}, categories: 
[]string{"drop"}},
+               multiColumnDictionaryTestRowGroup{ids: []int32{42}, categories: 
[]string{"keep"}},
+       )
+       rdr := openBloomTestReader(t, data)
+       defer rdr.Close()
+
+       var survivors []internal.RowGroupSpan
+       var calls int
+       tester := &internal.ParquetRowGroupTester{
+               StatsFieldIDs: []int{2},
+               Survivors:     &survivors,
+               StatsFn: func(rg *metadata.RowGroupMetaData, cols []int) (bool, 
error) {
+                       calls++
+                       require.Equal(t, []int{1}, cols, "stats must use the 
filter column, independent of projection")
+                       column, err := rg.ColumnChunk(cols[0])
+                       require.NoError(t, err)
+                       stats, err := column.Statistics()
+                       require.NoError(t, err)
+                       require.NotNil(t, stats)
+
+                       return bytes.Equal(stats.EncodeMin(), []byte("keep")), 
nil
+               },
+       }
+       rr, err := rdr.GetRecords(t.Context(), []int{0}, tester)
+       require.NoError(t, err)
+       defer rr.Release()
+
+       require.True(t, rr.Next())
+       record := rr.RecordBatch()
+       require.Equal(t, int64(1), record.NumCols(), "pruning must not add the 
filter column to the output")
+       assert.Equal(t, "id", record.Schema().Field(0).Name)
+       assert.Equal(t, []int32{42}, 
record.Column(0).(*array.Int32).Int32Values())
+       assert.False(t, rr.Next())
+       require.NoError(t, rr.Err())
+       assert.Equal(t, 2, calls)
+       assert.Equal(t, []internal.RowGroupSpan{{FirstRowPos: 1, NumRows: 1}}, 
survivors)
+}
+
+func TestParquetRowGroupStatsUseNestedFieldIDs(t *testing.T) {
+       before := schema.NewInt32Node("before", parquet.Repetitions.Required, 1)
+       target := schema.NewInt32Node("target", parquet.Repetitions.Required, 3)
+       other := schema.NewInt32Node("other", parquet.Repetitions.Required, 4)
+       nested, err := schema.NewGroupNode("nested", 
parquet.Repetitions.Required,
+               schema.FieldList{target, other}, 2)
+       require.NoError(t, err)
+       after := schema.NewInt32Node("after", parquet.Repetitions.Required, 5)
+       root, err := schema.NewGroupNode("schema", parquet.Repetitions.Required,
+               schema.FieldList{before, nested, after}, -1)
+       require.NoError(t, err)
+
+       var buf bytes.Buffer
+       writer := file.NewParquetWriter(&buf, root,
+               
file.WithWriterProps(parquet.NewWriterProperties(parquet.WithStats(true))))
+       rg, err := writer.AppendRowGroupChecked()
+       require.NoError(t, err)
+       for _, value := range []int32{10, 20, 30, 40} {
+               column, err := rg.NextColumn()
+               require.NoError(t, err)
+               _, err = 
column.(*file.Int32ColumnChunkWriter).WriteBatch([]int32{value}, nil, nil)
+               require.NoError(t, err)
+               require.NoError(t, column.Close())
+       }
+       require.NoError(t, rg.Close())
+       require.NoError(t, writer.Close())
+
+       rdr := openBloomTestReader(t, buf.Bytes())
+       defer rdr.Close()
+
+       var calls [][]int
+       tester := &internal.ParquetRowGroupTester{
+               StatsFn: func(_ *metadata.RowGroupMetaData, cols []int) (bool, 
error) {
+                       calls = append(calls, append([]int{}, cols...))
+
+                       return true, nil
+               },
+               StatsFieldIDs: []int{4},
+       }
+       rr, err := rdr.GetRecords(context.Background(), []int{0, 1, 2, 3}, 
tester)
+       require.NoError(t, err)
+       assert.Equal(t, int64(1), countRecords(t, rr))
+       require.Len(t, calls, 1)
+       assert.Equal(t, []int{2}, calls[0])
+}
+
+func TestParquetRowGroupStatsShareMappingWithBloomAndDictionaryPruning(t 
*testing.T) {
+       tests := []struct {
+               name       string
+               bloom      bool
+               dictionary bool
+       }{
+               {name: "stats only"},
+               {name: "stats and bloom", bloom: true},
+               {name: "stats and dictionary", dictionary: true},
+               {name: "stats and both", bloom: true, dictionary: true},
+       }
+       data := buildBloomTestParquet(t, 1)
+       for _, test := range tests {
+               t.Run(test.name, func(t *testing.T) {
+                       rdr := openBloomTestReader(t, data)
+                       defer rdr.Close()
+
+                       var calls [][]int
+                       tester := &internal.ParquetRowGroupTester{
+                               StatsFn: func(_ *metadata.RowGroupMetaData, 
cols []int) (bool, error) {
+                                       calls = append(calls, append([]int{}, 
cols...))
+
+                                       return true, nil
+                               },
+                               StatsFieldIDs: []int{1},
+                       }
+                       if test.bloom {
+                               tester.BloomPreds = 
[]internal.RowGroupBloomPred{{
+                                       FieldID: 1,
+                                       PhysBytes: [][]byte{
+                                               int32PhysBytes(1),
+                                               int32PhysBytes(2),
+                                       },
+                               }}
+                       }
+                       if test.dictionary {
+                               tester.DictionaryPreds = 
[]internal.RowGroupDictionaryPred{{
+                                       FieldID: 1,
+                                       PhysBytes: [][]byte{
+                                               int32PhysBytes(1),
+                                               int32PhysBytes(2),
+                                       },
+                               }}
+                       }
+
+                       rr, err := rdr.GetRecords(context.Background(), 
[]int{0}, tester)
+                       require.NoError(t, err)
+                       assert.Equal(t, int64(2), countRecords(t, rr))
+                       require.Len(t, calls, 2)
+                       assert.Equal(t, []int{0}, calls[0])
+                       assert.Equal(t, []int{0}, calls[1])
+               })
+       }
+}
diff --git a/table/row_group_stats_filter_columns_bench_test.go 
b/table/row_group_stats_filter_columns_bench_test.go
new file mode 100644
index 000000000..5bdae99a7
--- /dev/null
+++ b/table/row_group_stats_filter_columns_bench_test.go
@@ -0,0 +1,101 @@
+// 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 table
+
+import (
+       "fmt"
+       "testing"
+
+       "github.com/apache/iceberg-go"
+)
+
+var rowGroupStatsFilterColumnsBenchmarkSink int
+
+func BenchmarkParquetRowGroupStatsFilterColumns(b *testing.B) {
+       const rowGroups = 1024
+
+       for _, columnCount := range []int{32, 128} {
+               meta := buildRowGroupMetricsMetadata(b, rowGroups, columnCount, 
true)
+               fields := make([]iceberg.NestedField, columnCount)
+               readCols := make([]int, columnCount)
+               for i := range columnCount {
+                       fields[i] = iceberg.NestedField{
+                               ID: i + 1, Name: fmt.Sprintf("field_%d", i),
+                               Type: iceberg.PrimitiveTypes.String, Required: 
true,
+                       }
+                       readCols[i] = i
+               }
+               schema := iceberg.NewSchema(0, fields...)
+
+               filterCounts := []int{1, 4}
+               if columnCount == 128 {
+                       filterCounts = append(filterCounts, columnCount)
+               }
+               for _, filterCount := range filterCounts {
+                       var filter iceberg.BooleanExpression
+                       for i := range filterCount {
+                               pred := 
iceberg.EqualTo(iceberg.Reference(fmt.Sprintf("field_%d", i)), "m")
+                               if filter == nil {
+                                       filter = pred
+                               } else {
+                                       filter = iceberg.NewAnd(filter, pred)
+                               }
+                       }
+                       bound, err := iceberg.BindExpr(schema, filter, true)
+                       if err != nil {
+                               b.Fatal(err)
+                       }
+
+                       for _, test := range []struct {
+                               name string
+                               cols []int
+                       }{
+                               {name: "all_read_columns", cols: readCols},
+                               {name: "filter_columns", cols: 
readCols[:filterCount]},
+                       } {
+                               
b.Run(fmt.Sprintf("columns=%d/filter_columns=%d/%s", columnCount, filterCount, 
test.name), func(b *testing.B) {
+                                       eval, err := 
newParquetRowGroupStatsEvaluator(schema, bound, false)
+                                       if err != nil {
+                                               b.Fatal(err)
+                                       }
+
+                                       b.ReportAllocs()
+                                       b.ResetTimer()
+                                       for b.Loop() {
+                                               matched := 0
+                                               for rowGroup := range 
meta.NumRowGroups() {
+                                                       keep, err := 
eval(meta.RowGroup(rowGroup), test.cols)
+                                                       if err != nil {
+                                                               b.Fatal(err)
+                                                       }
+                                                       if keep {
+                                                               matched++
+                                                       }
+                                               }
+                                               
rowGroupStatsFilterColumnsBenchmarkSink = matched
+                                       }
+                                       b.StopTimer()
+
+                                       if 
rowGroupStatsFilterColumnsBenchmarkSink != rowGroups {
+                                               b.Fatalf("expected %d matches, 
got %d", rowGroups, rowGroupStatsFilterColumnsBenchmarkSink)
+                                       }
+                               })
+                       }
+               }
+       }
+}

Reply via email to