From 5119c0f0e7d2d24506cf5741eeff54ff17cbc7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20R=2E=20H=C3=B6lzlwimmer?= Date: Fri, 25 Sep 2026 19:53:52 +0000 Subject: [PATCH 1/2] feat(c-glib): add fixed closedness range GLib bindings Add GArrowFixedClosednessRangeDataType, the GLib binding for the arrow.fixed_closedness_range canonical extension type: a bounded interval over an orderable value type, with the inclusivity of its bounds fixed at the type level via the GArrowRangeClosed enum. Also adds a Ruby test covering construction, value_type, closed, the extension name, and the C++ round-trip. --- c_glib/arrow-glib/basic-data-type.cpp | 132 ++++++++++++++++++ c_glib/arrow-glib/basic-data-type.h | 51 +++++++ .../test-fixed-closedness-range-data-type.rb | 90 ++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 c_glib/test/test-fixed-closedness-range-data-type.rb diff --git a/c_glib/arrow-glib/basic-data-type.cpp b/c_glib/arrow-glib/basic-data-type.cpp index e7efcdcc124f..340acad2e3ad 100644 --- a/c_glib/arrow-glib/basic-data-type.cpp +++ b/c_glib/arrow-glib/basic-data-type.cpp @@ -27,6 +27,7 @@ #include #include +#include #include G_BEGIN_DECLS @@ -2591,6 +2592,135 @@ garrow_uuid_data_type_new(GError **error) return NULL; } } + +static arrow::extension::RangeClosed +garrow_range_closed_to_raw(GArrowRangeClosed closed) +{ + switch (closed) { + case GARROW_RANGE_CLOSED_LEFT: + return arrow::extension::RangeClosed::Left; + case GARROW_RANGE_CLOSED_RIGHT: + return arrow::extension::RangeClosed::Right; + case GARROW_RANGE_CLOSED_BOTH: + return arrow::extension::RangeClosed::Both; + case GARROW_RANGE_CLOSED_NEITHER: + return arrow::extension::RangeClosed::Neither; + default: + return arrow::extension::RangeClosed::Left; + } +} + +static GArrowRangeClosed +garrow_range_closed_from_raw(arrow::extension::RangeClosed closed) +{ + switch (closed) { + case arrow::extension::RangeClosed::Left: + return GARROW_RANGE_CLOSED_LEFT; + case arrow::extension::RangeClosed::Right: + return GARROW_RANGE_CLOSED_RIGHT; + case arrow::extension::RangeClosed::Both: + return GARROW_RANGE_CLOSED_BOTH; + case arrow::extension::RangeClosed::Neither: + return GARROW_RANGE_CLOSED_NEITHER; + default: + return GARROW_RANGE_CLOSED_LEFT; + } +} + +G_DEFINE_TYPE(GArrowFixedClosednessRangeDataType, + garrow_fixed_closedness_range_data_type, + GARROW_TYPE_EXTENSION_DATA_TYPE) + +static void +garrow_fixed_closedness_range_data_type_init(GArrowFixedClosednessRangeDataType *object) +{ +} + +static void +garrow_fixed_closedness_range_data_type_class_init( + GArrowFixedClosednessRangeDataTypeClass *klass) +{ +} + +/** + * garrow_fixed_closedness_range_data_type_new: + * @value_type: The orderable #GArrowDataType of the lower and upper bounds. + * @closed: Which bound(s) of the interval are inclusive. + * @allow_unbounded: Whether each side may be unbounded (infinite); when %TRUE + * the lower/upper fields are nullable, when %FALSE they are non-nullable and + * the range is always finite. + * @error: (nullable): Return location for a #GError or %NULL. + * + * Returns: (nullable): + * The newly created fixed closedness range data type on success, %NULL on + * error. + * + * Since: 26.0.0 + */ +GArrowFixedClosednessRangeDataType * +garrow_fixed_closedness_range_data_type_new(GArrowDataType *value_type, + GArrowRangeClosed closed, + gboolean allow_unbounded, + GError **error) +{ + auto arrow_value_type = garrow_data_type_get_raw(value_type); + auto arrow_closed = garrow_range_closed_to_raw(closed); + auto arrow_data_type_result = + arrow::extension::FixedClosednessRangeType::Make(arrow_value_type, + arrow_closed, + allow_unbounded); + if (garrow::check(error, + arrow_data_type_result, + "[fixed-closedness-range-data-type][new]")) { + auto arrow_data_type = *arrow_data_type_result; + return GARROW_FIXED_CLOSEDNESS_RANGE_DATA_TYPE( + g_object_new(GARROW_TYPE_FIXED_CLOSEDNESS_RANGE_DATA_TYPE, + "data-type", + &arrow_data_type, + NULL)); + } else { + return NULL; + } +} + +/** + * garrow_fixed_closedness_range_data_type_get_value_type: + * @data_type: A #GArrowFixedClosednessRangeDataType. + * + * Returns: (transfer full): The orderable #GArrowDataType of the lower + * and upper bounds. + * + * Since: 26.0.0 + */ +GArrowDataType * +garrow_fixed_closedness_range_data_type_get_value_type( + GArrowFixedClosednessRangeDataType *data_type) +{ + auto arrow_data_type = + std::static_pointer_cast( + garrow_data_type_get_raw(GARROW_DATA_TYPE(data_type))); + auto arrow_value_type = arrow_data_type->value_type(); + return garrow_data_type_new_raw(&arrow_value_type); +} + +/** + * garrow_fixed_closedness_range_data_type_get_closed: + * @data_type: A #GArrowFixedClosednessRangeDataType. + * + * Returns: Which bound(s) of the interval are inclusive. + * + * Since: 26.0.0 + */ +GArrowRangeClosed +garrow_fixed_closedness_range_data_type_get_closed( + GArrowFixedClosednessRangeDataType *data_type) +{ + auto arrow_data_type = + std::static_pointer_cast( + garrow_data_type_get_raw(GARROW_DATA_TYPE(data_type))); + return garrow_range_closed_from_raw(arrow_data_type->closed()); +} + G_END_DECLS GArrowDataType * @@ -2746,6 +2876,8 @@ garrow_data_type_new_raw(std::shared_ptr *arrow_data_type) type = GARROW_TYPE_FIXED_SHAPE_TENSOR_DATA_TYPE; } else if (name == "arrow.uuid") { type = GARROW_TYPE_UUID_DATA_TYPE; + } else if (name == "arrow.fixed_closedness_range") { + type = GARROW_TYPE_FIXED_CLOSEDNESS_RANGE_DATA_TYPE; } else { auto g_extension_data_type = std::dynamic_pointer_cast(*arrow_data_type); diff --git a/c_glib/arrow-glib/basic-data-type.h b/c_glib/arrow-glib/basic-data-type.h index 21f4be7c0c60..8df247c6afa0 100644 --- a/c_glib/arrow-glib/basic-data-type.h +++ b/c_glib/arrow-glib/basic-data-type.h @@ -894,4 +894,55 @@ struct _GArrowUUIDDataTypeClass GARROW_AVAILABLE_IN_21_0 GArrowUUIDDataType * garrow_uuid_data_type_new(GError **error); + +/** + * GArrowRangeClosed: + * @GARROW_RANGE_CLOSED_LEFT: Lower bound is inclusive, upper bound is + * exclusive: [lower, upper). + * @GARROW_RANGE_CLOSED_RIGHT: Lower bound is exclusive, upper bound is + * inclusive: (lower, upper]. + * @GARROW_RANGE_CLOSED_BOTH: Both bounds are inclusive: [lower, upper]. + * @GARROW_RANGE_CLOSED_NEITHER: Both bounds are exclusive: (lower, upper). + * + * They are corresponding to `arrow::extension::RangeClosed` values. + * + * Since: 26.0.0 + */ +typedef enum { + GARROW_RANGE_CLOSED_LEFT, + GARROW_RANGE_CLOSED_RIGHT, + GARROW_RANGE_CLOSED_BOTH, + GARROW_RANGE_CLOSED_NEITHER, +} GArrowRangeClosed; + +#define GARROW_TYPE_FIXED_CLOSEDNESS_RANGE_DATA_TYPE \ + (garrow_fixed_closedness_range_data_type_get_type()) +GARROW_AVAILABLE_IN_26_0 +G_DECLARE_DERIVABLE_TYPE(GArrowFixedClosednessRangeDataType, + garrow_fixed_closedness_range_data_type, + GARROW, + FIXED_CLOSEDNESS_RANGE_DATA_TYPE, + GArrowExtensionDataType) +struct _GArrowFixedClosednessRangeDataTypeClass +{ + GArrowExtensionDataTypeClass parent_class; +}; + +GARROW_AVAILABLE_IN_26_0 +GArrowFixedClosednessRangeDataType * +garrow_fixed_closedness_range_data_type_new(GArrowDataType *value_type, + GArrowRangeClosed closed, + gboolean allow_unbounded, + GError **error); + +GARROW_AVAILABLE_IN_26_0 +GArrowDataType * +garrow_fixed_closedness_range_data_type_get_value_type( + GArrowFixedClosednessRangeDataType *data_type); + +GARROW_AVAILABLE_IN_26_0 +GArrowRangeClosed +garrow_fixed_closedness_range_data_type_get_closed( + GArrowFixedClosednessRangeDataType *data_type); + G_END_DECLS diff --git a/c_glib/test/test-fixed-closedness-range-data-type.rb b/c_glib/test/test-fixed-closedness-range-data-type.rb new file mode 100644 index 000000000000..a96b3a11edc5 --- /dev/null +++ b/c_glib/test/test-fixed-closedness-range-data-type.rb @@ -0,0 +1,90 @@ +# 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. + +class TestFixedClosednessRangeDataType < Test::Unit::TestCase + def setup + @value_type = Arrow::Int32DataType.new + @data_type = Arrow::FixedClosednessRangeDataType.new(@value_type, + Arrow::RangeClosed::LEFT, + true) + end + + def test_id + assert_equal(Arrow::Type::EXTENSION, @data_type.id) + end + + def test_name + assert_equal(["extension", "arrow.fixed_closedness_range"], + [@data_type.name, @data_type.extension_name]) + end + + def test_value_type + assert_equal(@value_type, @data_type.value_type) + end + + def test_closed + assert_equal(Arrow::RangeClosed::LEFT, @data_type.closed) + end + + def test_to_s + assert do + @data_type.to_s.start_with?("extension Date: Fri, 25 Sep 2026 19:54:13 +0000 Subject: [PATCH 2/2] feat(c-glib): add variable closedness range GLib bindings Add GArrowVariableClosednessRangeDataType, the GLib binding for the arrow.variable_closedness_range canonical extension type. The inclusivity of each bound is stored per value, so construction takes no closed argument. A Ruby test covers construction, value_type, the extension name, and the C++ round trip. --- c_glib/arrow-glib/basic-data-type.cpp | 79 +++++++++++++++++++ c_glib/arrow-glib/basic-data-type.h | 24 ++++++ ...est-variable-closedness-range-data-type.rb | 55 +++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 c_glib/test/test-variable-closedness-range-data-type.rb diff --git a/c_glib/arrow-glib/basic-data-type.cpp b/c_glib/arrow-glib/basic-data-type.cpp index 340acad2e3ad..848256e1d167 100644 --- a/c_glib/arrow-glib/basic-data-type.cpp +++ b/c_glib/arrow-glib/basic-data-type.cpp @@ -2721,6 +2721,83 @@ garrow_fixed_closedness_range_data_type_get_closed( return garrow_range_closed_from_raw(arrow_data_type->closed()); } +G_DEFINE_TYPE(GArrowVariableClosednessRangeDataType, + garrow_variable_closedness_range_data_type, + GARROW_TYPE_EXTENSION_DATA_TYPE) + +static void +garrow_variable_closedness_range_data_type_init( + GArrowVariableClosednessRangeDataType *object) +{ +} + +static void +garrow_variable_closedness_range_data_type_class_init( + GArrowVariableClosednessRangeDataTypeClass *klass) +{ +} + +/** + * garrow_variable_closedness_range_data_type_new: + * @value_type: The orderable #GArrowDataType of the lower and upper bounds. + * @allow_unbounded: Whether each side may be unbounded (infinite); when %TRUE + * the lower/upper fields are nullable, when %FALSE they are non-nullable and + * the range is always finite. The lower_inc/upper_inc fields are always + * non-nullable. + * @error: (nullable): Return location for a #GError or %NULL. + * + * Unlike #GArrowFixedClosednessRangeDataType, the inclusivity of each bound + * is stored per value in boolean lower_inc/upper_inc fields rather than as a + * single type-level parameter, so there is no closed argument. + * + * Returns: (nullable): + * The newly created variable closedness range data type on success, %NULL + * on error. + * + * Since: 26.0.0 + */ +GArrowVariableClosednessRangeDataType * +garrow_variable_closedness_range_data_type_new(GArrowDataType *value_type, + gboolean allow_unbounded, + GError **error) +{ + auto arrow_value_type = garrow_data_type_get_raw(value_type); + auto arrow_data_type_result = + arrow::extension::VariableClosednessRangeType::Make(arrow_value_type, + allow_unbounded); + if (garrow::check(error, + arrow_data_type_result, + "[variable-closedness-range-data-type][new]")) { + auto arrow_data_type = *arrow_data_type_result; + return GARROW_VARIABLE_CLOSEDNESS_RANGE_DATA_TYPE( + g_object_new(GARROW_TYPE_VARIABLE_CLOSEDNESS_RANGE_DATA_TYPE, + "data-type", + &arrow_data_type, + NULL)); + } else { + return NULL; + } +} + +/** + * garrow_variable_closedness_range_data_type_get_value_type: + * @data_type: A #GArrowVariableClosednessRangeDataType. + * + * Returns: (transfer full): The orderable #GArrowDataType of the lower + * and upper bounds. + * + * Since: 26.0.0 + */ +GArrowDataType * +garrow_variable_closedness_range_data_type_get_value_type( + GArrowVariableClosednessRangeDataType *data_type) +{ + auto arrow_data_type = + std::static_pointer_cast( + garrow_data_type_get_raw(GARROW_DATA_TYPE(data_type))); + auto arrow_value_type = arrow_data_type->value_type(); + return garrow_data_type_new_raw(&arrow_value_type); +} G_END_DECLS GArrowDataType * @@ -2878,6 +2955,8 @@ garrow_data_type_new_raw(std::shared_ptr *arrow_data_type) type = GARROW_TYPE_UUID_DATA_TYPE; } else if (name == "arrow.fixed_closedness_range") { type = GARROW_TYPE_FIXED_CLOSEDNESS_RANGE_DATA_TYPE; + } else if (name == "arrow.variable_closedness_range") { + type = GARROW_TYPE_VARIABLE_CLOSEDNESS_RANGE_DATA_TYPE; } else { auto g_extension_data_type = std::dynamic_pointer_cast(*arrow_data_type); diff --git a/c_glib/arrow-glib/basic-data-type.h b/c_glib/arrow-glib/basic-data-type.h index 8df247c6afa0..36c5a20d9050 100644 --- a/c_glib/arrow-glib/basic-data-type.h +++ b/c_glib/arrow-glib/basic-data-type.h @@ -945,4 +945,28 @@ GArrowRangeClosed garrow_fixed_closedness_range_data_type_get_closed( GArrowFixedClosednessRangeDataType *data_type); +#define GARROW_TYPE_VARIABLE_CLOSEDNESS_RANGE_DATA_TYPE \ + (garrow_variable_closedness_range_data_type_get_type()) +GARROW_AVAILABLE_IN_26_0 +G_DECLARE_DERIVABLE_TYPE(GArrowVariableClosednessRangeDataType, + garrow_variable_closedness_range_data_type, + GARROW, + VARIABLE_CLOSEDNESS_RANGE_DATA_TYPE, + GArrowExtensionDataType) +struct _GArrowVariableClosednessRangeDataTypeClass +{ + GArrowExtensionDataTypeClass parent_class; +}; + +GARROW_AVAILABLE_IN_26_0 +GArrowVariableClosednessRangeDataType * +garrow_variable_closedness_range_data_type_new(GArrowDataType *value_type, + gboolean allow_unbounded, + GError **error); + +GARROW_AVAILABLE_IN_26_0 +GArrowDataType * +garrow_variable_closedness_range_data_type_get_value_type( + GArrowVariableClosednessRangeDataType *data_type); + G_END_DECLS diff --git a/c_glib/test/test-variable-closedness-range-data-type.rb b/c_glib/test/test-variable-closedness-range-data-type.rb new file mode 100644 index 000000000000..414a35fafa0e --- /dev/null +++ b/c_glib/test/test-variable-closedness-range-data-type.rb @@ -0,0 +1,55 @@ +# 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. + +class TestVariableClosednessRangeDataType < Test::Unit::TestCase + def setup + @value_type = Arrow::Int32DataType.new + @data_type = Arrow::VariableClosednessRangeDataType.new(@value_type, true) + end + + def test_id + assert_equal(Arrow::Type::EXTENSION, @data_type.id) + end + + def test_name + assert_equal(["extension", "arrow.variable_closedness_range"], + [@data_type.name, @data_type.extension_name]) + end + + def test_value_type + assert_equal(@value_type, @data_type.value_type) + end + + def test_to_s + assert do + @data_type.to_s.start_with?("extension