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

kou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new 91363944722 GH-50571: [Ruby] Add schema-aware ArrowFormat::RecordBatch 
construction (#51214)
91363944722 is described below

commit 91363944722418a75f480040af0b643ecf369be5
Author: Yifan Chen <[email protected]>
AuthorDate: Tue Sep 8 11:25:45 2026 -0700

    GH-50571: [Ruby] Add schema-aware ArrowFormat::RecordBatch construction 
(#51214)
    
    ### Rationale for this change
    
    `ArrowFormat::RecordBatch.new(values)` infers a schema from Ruby values. 
This adds the corresponding schema-aware constructor so callers can preserve 
intended Arrow types while building from Ruby rows or columns.
    
    ### What changes are included in this PR?
    
    * Add `ArrowFormat::RecordBatch.new(schema, values)` for row- and 
column-oriented Ruby values.
    * Build each column through its schema field type and preserve null values.
    * Cover type preservation, supported value forms, inconsistent column 
lengths, and invalid extra values.
    
    ### Are these changes tested?
    
    Yes. The `red-arrow-format` suite passes locally (690 tests, 693 
assertions), including the focused record-batch tests. The existing `red-arrow` 
record-batch suite also passes (21 tests, 21 assertions).
    
    ### Are there any user-facing changes?
    
    Yes.
    
    ### AI assistance
    
    AI assistance drafted the implementation and tests. The final scope was 
checked against the existing two-argument `Arrow::RecordBatch.new` contract and 
the one-argument `ArrowFormat::RecordBatch` API; the validation commands above 
were run locally.
    
    * GitHub Issue: #50571
    
    Authored-by: Yifan Chen <[email protected]>
    Signed-off-by: Sutou Kouhei <[email protected]>
---
 .../lib/arrow-format/record-batch.rb               | 60 ++++++++++++++-
 ruby/red-arrow-format/test/test-record-batch.rb    | 86 ++++++++++++++++++++++
 2 files changed, 144 insertions(+), 2 deletions(-)

diff --git a/ruby/red-arrow-format/lib/arrow-format/record-batch.rb 
b/ruby/red-arrow-format/lib/arrow-format/record-batch.rb
index db2b2dbe8fc..776621dc084 100644
--- a/ruby/red-arrow-format/lib/arrow-format/record-batch.rb
+++ b/ruby/red-arrow-format/lib/arrow-format/record-batch.rb
@@ -30,9 +30,14 @@ module ArrowFormat
     attr_reader :message_metadata
     def initialize(*args, message_metadata: nil)
       n_args = args.size
-      args = build(args[0]) if n_args == 1
+      case n_args
+      when 1
+        args = build(args[0])
+      when 2
+        args = build_with_schema(*args)
+      end
       if args.size != 3
-        message = "wrong number of arguments (given #{n_args}, expected 1 or 
3)"
+        message = "wrong number of arguments (given #{n_args}, expected 1..3)"
         raise ArgumentError, message
       end
       schema, n_rows, columns = args
@@ -143,6 +148,57 @@ module ArrowFormat
     end
 
     private
+    def build_with_schema(schema, data)
+      fields = schema.fields
+      name_to_index = {}
+      fields.each_with_index do |field, i|
+        name_to_index[field.name] = i
+      end
+      if data.is_a?(Hash)
+        raw_columns = []
+        data.each do |name, values|
+          field_index = name_to_index[name.to_s]
+          raw_columns[field_index] = values if field_index
+        end
+        columns = fields.zip(raw_columns).collect do |field, values|
+          field.type.build_array(values || [])
+        end
+        all_n_rows = columns.collect(&:size)
+        if all_n_rows.uniq.size != 1
+          message = "inconsistent the number of rows: #{all_n_rows.join(", ")}"
+          raise ArgumentError, message
+        end
+      else
+        raw_columns = fields.collect { [] }
+        data.each_with_index do |record, nth_record|
+          case record
+          when nil
+          when Hash
+            record.each do |name, value|
+              field_index = name_to_index[name.to_s]
+              raw_columns[field_index] << value if field_index
+            end
+          else
+            if record.size > raw_columns.size
+              message = "row #{nth_record} has more values than schema fields"
+              raise ArgumentError, message
+            end
+            record.each_with_index do |value, field_index|
+              raw_columns[field_index] << value
+            end
+          end
+          raw_columns.each do |column|
+            column << nil if column.size != nth_record + 1
+          end
+        end
+        columns = fields.zip(raw_columns).collect do |field, values|
+          field.type.build_array(values)
+        end
+      end
+      n_rows = columns.first&.size || 0
+      [schema, n_rows, columns]
+    end
+
     def build(data)
       records = nil
       fields = []
diff --git a/ruby/red-arrow-format/test/test-record-batch.rb 
b/ruby/red-arrow-format/test/test-record-batch.rb
index 30bd79fc35a..9a082bebfa6 100644
--- a/ruby/red-arrow-format/test/test-record-batch.rb
+++ b/ruby/red-arrow-format/test/test-record-batch.rb
@@ -26,6 +26,92 @@ class TestRecordBatch < Test::Unit::TestCase
   end
 
   sub_test_case("#initialize") do
+    sub_test_case("[Schema, values]") do
+      def setup
+        @schema = ArrowFormat::Schema.new([
+                                             ArrowFormat::Field.new(
+                                               "visible",
+                                               
ArrowFormat::BooleanType.singleton,
+                                             ),
+                                             ArrowFormat::Field.new(
+                                               "count",
+                                               
ArrowFormat::UInt32Type.singleton,
+                                             ),
+                                           ])
+      end
+
+      test("records") do
+        record_batch = ArrowFormat::RecordBatch.new(
+          @schema,
+          [
+            {visible: true, count: 1},
+            nil,
+            [false, 3],
+          ],
+        )
+        assert_equal(@schema, record_batch.schema)
+        assert_equal(ArrowFormat::BooleanArray,
+                     record_batch.find_column("visible").class)
+        assert_equal(ArrowFormat::UInt32Array,
+                     record_batch.find_column("count").class)
+        assert_equal([
+                       {"visible" => true,  "count" => 1},
+                       {"visible" => nil,   "count" => nil},
+                       {"visible" => false, "count" => 3},
+                     ],
+                     record_batch.records.collect(&:to_h))
+      end
+
+      test("columns") do
+        record_batch = ArrowFormat::RecordBatch.new(
+          @schema,
+          {
+            visible: [true, nil, false],
+            "count" => [1, 2, nil],
+          },
+        )
+        assert_equal([
+                       {"visible" => true,  "count" => 1},
+                       {"visible" => nil,   "count" => 2},
+                       {"visible" => false, "count" => nil},
+                     ],
+                     record_batch.records.collect(&:to_h))
+      end
+
+      test("inconsistent column lengths") do
+        error = ArgumentError.new("inconsistent the number of rows: 2, 3")
+        assert_raise(error) do
+          ArrowFormat::RecordBatch.new(
+            @schema,
+            {
+              visible: [true, nil],
+              count: [1, 2, 3],
+            },
+          )
+        end
+      end
+
+      test("unknown column") do
+        record_batch = ArrowFormat::RecordBatch.new(
+          @schema,
+          {
+            visible: [true],
+            count: [1],
+            extra: [2],
+          },
+        )
+        assert_equal([{"visible" => true, "count" => 1}],
+                     record_batch.records.collect(&:to_h))
+      end
+
+      test("too many row values") do
+        error = ArgumentError.new("row 0 has more values than schema fields")
+        assert_raise(error) do
+          ArrowFormat::RecordBatch.new(@schema, [[true, 1, 2]])
+        end
+      end
+    end
+
     test("{}") do
       error = ArgumentError.new("no data")
       assert_raise(error) do

Reply via email to