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