Repository: apache/arrow-rs
Pull request #6433 (2024-09-21 09:01:47)

chore: add docs, part of #37
- add pragma `#![warn(missing_docs)]` to the following
  - `arrow-array`
  - `arrow-cast`
  - `arrow-csv`
  - `arrow-data`
  - `arrow-json`
  - `arrow-ord`
  - `arrow-pyarrow-integration-testing`
  - `arrow-row`
  - `arrow-schema`
  - `arrow-select`
  - `arrow-string`
  - `arrow`
  - `parquet_derive`

- add docs to those that generated lint warnings

- Remove `bitflags` workaround in `arrow-schema`. At some point, a change in `bitflags v2.3.0` had started generating lint warnings in `arrow-schema`. This was handled using a [workaround](https://github.com/apache/arrow-rs/pull/4233). Here is [the original issue](https://github.com/bitflags/bitflags/issues/356).

`bitflags v2.3.1` fixed the issue hence the workaround is no longer needed.


ByteBaker: @alamb for your perusal.

Patch:
diff --git a/arrow-array/Cargo.toml b/arrow-array/Cargo.toml
index 57b86c192..d993d36b8 100644
--- a/arrow-array/Cargo.toml
+++ b/arrow-array/Cargo.toml
@@ -71,3 +71,7 @@ harness = false
 [[bench]]
 name = "fixed_size_list_array"
 harness = false
+
+[[bench]]
+name = "decimal_overflow"
+harness = false
diff --git a/arrow-array/benches/decimal_overflow.rs b/arrow-array/benches/decimal_overflow.rs
new file mode 100644
index 000000000..8f22b4b47
--- /dev/null
+++ b/arrow-array/benches/decimal_overflow.rs
@@ -0,0 +1,53 @@
+// 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.
+
+use arrow_array::builder::{Decimal128Builder, Decimal256Builder};
+use arrow_buffer::i256;
+use criterion::*;
+
+fn criterion_benchmark(c: &mut Criterion) {
+    let len = 8192;
+    let mut builder_128 = Decimal128Builder::with_capacity(len);
+    let mut builder_256 = Decimal256Builder::with_capacity(len);
+    for i in 0..len {
+        if i % 10 == 0 {
+            builder_128.append_value(i128::MAX);
+            builder_256.append_value(i256::from_i128(i128::MAX));
+        } else {
+            builder_128.append_value(i as i128);
+            builder_256.append_value(i256::from_i128(i as i128));
+        }
+    }
+    let array_128 = builder_128.finish();
+    let array_256 = builder_256.finish();
+
+    c.bench_function("validate_decimal_precision_128", |b| {
+        b.iter(|| black_box(array_128.validate_decimal_precision(8)));
+    });
+    c.bench_function("null_if_overflow_precision_128", |b| {
+        b.iter(|| black_box(array_128.null_if_overflow_precision(8)));
+    });
+    c.bench_function("validate_decimal_precision_256", |b| {
+        b.iter(|| black_box(array_256.validate_decimal_precision(8)));
+    });
+    c.bench_function("null_if_overflow_precision_256", |b| {
+        b.iter(|| black_box(array_256.null_if_overflow_precision(8)));
+    });
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/arrow-array/src/array/primitive_array.rs b/arrow-array/src/array/primitive_array.rs
index 521ef088e..567fa00e7 100644
--- a/arrow-array/src/array/primitive_array.rs
+++ b/arrow-array/src/array/primitive_array.rs
@@ -1570,9 +1570,7 @@ impl<T: DecimalType + ArrowPrimitiveType> PrimitiveArray<T> {
     /// Validates the Decimal Array, if the value of slot is overflow for the specified precision, and
     /// will be casted to Null
     pub fn null_if_overflow_precision(&self, precision: u8) -> Self {
-        self.unary_opt::<_, T>(|v| {
-            (T::validate_decimal_precision(v, precision).is_ok()).then_some(v)
-        })
+        self.unary_opt::<_, T>(|v| T::is_valid_decimal_precision(v, precision).then_some(v))
     }
 
     /// Returns [`Self::value`] formatted as a string
diff --git a/arrow-array/src/builder/generic_bytes_builder.rs b/arrow-array/src/builder/generic_bytes_builder.rs
index e85a2fed0..a465f3e4d 100644
--- a/arrow-array/src/builder/generic_bytes_builder.rs
+++ b/arrow-array/src/builder/generic_bytes_builder.rs
@@ -537,7 +537,7 @@ mod tests {
         write!(builder, "buz").unwrap();
         builder.append_value("");
         let a = builder.finish();
-        let r: Vec<_> = a.iter().map(|x| x.unwrap()).collect();
+        let r: Vec<_> = a.iter().flatten().collect();
         assert_eq!(r, &["foo", "bar\n", "fizbuz"])
     }
 }
diff --git a/arrow-array/src/builder/generic_bytes_view_builder.rs b/arrow-array/src/builder/generic_bytes_view_builder.rs
index 09277c679..d12c2b7db 100644
--- a/arrow-array/src/builder/generic_bytes_view_builder.rs
+++ b/arrow-array/src/builder/generic_bytes_view_builder.rs
@@ -555,6 +555,8 @@ pub fn make_view(data: &[u8], block_id: u32, offset: u32) -> u128 {
 
 #[cfg(test)]
 mod tests {
+    use core::str;
+
     use super::*;
     use crate::Array;
 
@@ -642,7 +644,7 @@ mod tests {
         let array = v.finish_cloned();
         array.to_data().validate_full().unwrap();
         assert_eq!(array.data_buffers().len(), 5);
-        let actual: Vec<_> = array.iter().map(Option::unwrap).collect();
+        let actual: Vec<_> = array.iter().flatten().collect();
         assert_eq!(
             actual,
             &[
@@ -692,13 +694,13 @@ mod tests {
         let mut exp_builder = StringViewBuilder::new();
         let mut fixed_builder = StringViewBuilder::new().with_fixed_block_size(STARTING_BLOCK_SIZE);
 
-        let long_string = String::from_utf8(vec![b'a'; STARTING_BLOCK_SIZE as usize]).unwrap();
+        let long_string = str::from_utf8(&[b'a'; STARTING_BLOCK_SIZE as usize]).unwrap();
 
         for i in 0..9 {
             // 8k, 16k, 32k, 64k, 128k, 256k, 512k, 1M, 2M
             for _ in 0..(2_u32.pow(i)) {
-                exp_builder.append_value(&long_string);
-                fixed_builder.append_value(&long_string);
+                exp_builder.append_value(long_string);
+                fixed_builder.append_value(long_string);
             }
             exp_builder.flush_in_progress();
             fixed_builder.flush_in_progress();
@@ -721,7 +723,7 @@ mod tests {
         }
 
         // Add one more value, and the buffer stop growing.
-        exp_builder.append_value(&long_string);
+        exp_builder.append_value(long_string);
         exp_builder.flush_in_progress();
         assert_eq!(
             exp_builder.completed.last().unwrap().capacity(),
diff --git a/arrow-array/src/types.rs b/arrow-array/src/types.rs
index b39c9c403..92262fc04 100644
--- a/arrow-array/src/types.rs
+++ b/arrow-array/src/types.rs
@@ -24,7 +24,10 @@ use crate::temporal_conversions::as_datetime_with_timezone;
 use crate::timezone::Tz;
 use crate::{ArrowNativeTypeOp, OffsetSizeTrait};
 use arrow_buffer::{i256, Buffer, OffsetBuffer};
-use arrow_data::decimal::{validate_decimal256_precision, validate_decimal_precision};
+use arrow_data::decimal::{
+    is_validate_decimal256_precision, is_validate_decimal_precision, validate_decimal256_precision,
+    validate_decimal_precision,
+};
 use arrow_data::{validate_binary_view, validate_string_view};
 use arrow_schema::{
     ArrowError, DataType, IntervalUnit, TimeUnit, DECIMAL128_MAX_PRECISION, DECIMAL128_MAX_SCALE,
@@ -1194,6 +1197,9 @@ pub trait DecimalType:
 
     /// Validates that `value` contains no more than `precision` decimal digits
     fn validate_decimal_precision(value: Self::Native, precision: u8) -> Result<(), ArrowError>;
+
+    /// Determines whether `value` contains no more than `precision` decimal digits
+    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool;
 }
 
 /// Validate that `precision` and `scale` are valid for `T`
@@ -1256,6 +1262,10 @@ impl DecimalType for Decimal128Type {
     fn validate_decimal_precision(num: i128, precision: u8) -> Result<(), ArrowError> {
         validate_decimal_precision(num, precision)
     }
+
+    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool {
+        is_validate_decimal_precision(value, precision)
+    }
 }
 
 impl ArrowPrimitiveType for Decimal128Type {
@@ -1286,6 +1296,10 @@ impl DecimalType for Decimal256Type {
     fn validate_decimal_precision(num: i256, precision: u8) -> Result<(), ArrowError> {
         validate_decimal256_precision(num, precision)
     }
+
+    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool {
+        is_validate_decimal256_precision(value, precision)
+    }
 }
 
 impl ArrowPrimitiveType for Decimal256Type {
diff --git a/arrow-cast/src/cast/decimal.rs b/arrow-cast/src/cast/decimal.rs
index 600f868a3..637cbc417 100644
--- a/arrow-cast/src/cast/decimal.rs
+++ b/arrow-cast/src/cast/decimal.rs
@@ -336,11 +336,7 @@ where
     if cast_options.safe {
         let iter = from.iter().map(|v| {
             v.and_then(|v| parse_string_to_decimal_native::<T>(v, scale as usize).ok())
-                .and_then(|v| {
-                    T::validate_decimal_precision(v, precision)
-                        .is_ok()
-                        .then_some(v)
-                })
+                .and_then(|v| T::is_valid_decimal_precision(v, precision).then_some(v))
         });
         // Benefit:
         //     20% performance improvement
@@ -430,7 +426,7 @@ where
                 (mul * v.as_())
                     .round()
                     .to_i128()
-                    .filter(|v| Decimal128Type::validate_decimal_precision(*v, precision).is_ok())
+                    .filter(|v| Decimal128Type::is_valid_decimal_precision(*v, precision))
             })
             .with_precision_and_scale(precision, scale)
             .map(|a| Arc::new(a) as ArrayRef)
@@ -473,7 +469,7 @@ where
         array
             .unary_opt::<_, Decimal256Type>(|v| {
                 i256::from_f64((v.as_() * mul).round())
-                    .filter(|v| Decimal256Type::validate_decimal_precision(*v, precision).is_ok())
+                    .filter(|v| Decimal256Type::is_valid_decimal_precision(*v, precision))
             })
             .with_precision_and_scale(precision, scale)
             .map(|a| Arc::new(a) as ArrayRef)
diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs
index e80d497c8..b751c81ee 100644
--- a/arrow-cast/src/cast/mod.rs
+++ b/arrow-cast/src/cast/mod.rs
@@ -327,9 +327,10 @@ where
     let array = if scale < 0 {
         match cast_options.safe {
             true => array.unary_opt::<_, D>(|v| {
-                v.as_().div_checked(scale_factor).ok().and_then(|v| {
-                    (D::validate_decimal_precision(v, precision).is_ok()).then_some(v)
-                })
+                v.as_()
+                    .div_checked(scale_factor)
+                    .ok()
+                    .and_then(|v| (D::is_valid_decimal_precision(v, precision)).then_some(v))
             }),
             false => array.try_unary::<_, D, _>(|v| {
                 v.as_()
@@ -340,9 +341,10 @@ where
     } else {
         match cast_options.safe {
             true => array.unary_opt::<_, D>(|v| {
-                v.as_().mul_checked(scale_factor).ok().and_then(|v| {
-                    (D::validate_decimal_precision(v, precision).is_ok()).then_some(v)
-                })
+                v.as_()
+                    .mul_checked(scale_factor)
+                    .ok()
+                    .and_then(|v| (D::is_valid_decimal_precision(v, precision)).then_some(v))
             }),
             false => array.try_unary::<_, D, _>(|v| {
                 v.as_()
@@ -9392,7 +9394,7 @@ mod tests {
             Some(vec![Some(0), None, Some(2)]),
         ]);
         let a = cast_with_options(&array, &DataType::Utf8, &options).unwrap();
-        let r: Vec<_> = a.as_string::<i32>().iter().map(|x| x.unwrap()).collect();
+        let r: Vec<_> = a.as_string::<i32>().iter().flatten().collect();
         assert_eq!(r, &["[0, 1, 2]", "[0, null, 2]"]);
     }
     #[test]
diff --git a/arrow-csv/src/writer.rs b/arrow-csv/src/writer.rs
index dded475aa..eae2133a4 100644
--- a/arrow-csv/src/writer.rs
+++ b/arrow-csv/src/writer.rs
@@ -442,6 +442,7 @@ mod tests {
     };
     use arrow_array::types::*;
     use arrow_buffer::i256;
+    use core::str;
     use std::io::{Cursor, Read, Seek};
     use std::sync::Arc;
 
@@ -508,7 +509,7 @@ Lorem ipsum dolor sit amet,123.564532,3,true,,00:20:34,cupcakes
 consectetur adipiscing elit,,2,false,2019-04-18T10:54:47.378,06:51:20,cupcakes
 sed do eiusmod tempor,-556132.25,1,,2019-04-18T02:45:55.555,23:46:03,foo
 "#;
-        assert_eq!(expected.to_string(), String::from_utf8(buffer).unwrap());
+        assert_eq!(expected, str::from_utf8(&buffer).unwrap());
     }
 
     #[test]
@@ -558,7 +559,7 @@ sed do eiusmod tempor,-556132.25,1,,2019-04-18T02:45:55.555,23:46:03,foo
 ,
 0.290472,0.290472
 "#;
-        assert_eq!(expected.to_string(), String::from_utf8(buffer).unwrap());
+        assert_eq!(expected, str::from_utf8(&buffer).unwrap());
     }
 
     #[test]
diff --git a/arrow-data/src/byte_view.rs b/arrow-data/src/byte_view.rs
index a2e9d135f..6f6d6d175 100644
--- a/arrow-data/src/byte_view.rs
+++ b/arrow-data/src/byte_view.rs
@@ -40,6 +40,7 @@ pub struct ByteView {
 
 impl ByteView {
     #[inline(always)]
+    /// Convert `ByteView` to `u128` by concatenating the fields
     pub fn as_u128(self) -> u128 {
         (self.length as u128)
             | ((self.prefix as u128) << 32)
diff --git a/arrow-data/src/data.rs b/arrow-data/src/data.rs
index 33cbc897a..8c9e002e2 100644
--- a/arrow-data/src/data.rs
+++ b/arrow-data/src/data.rs
@@ -231,6 +231,7 @@ pub struct ArrayData {
     nulls: Option<NullBuffer>,
 }
 
+/// A thread-safe, shared reference to the Arrow array data.
 pub type ArrayDataRef = Arc<ArrayData>;
 
 impl ArrayData {
@@ -1747,7 +1748,12 @@ pub enum BufferSpec {
     /// for array slicing and interoperability with `Vec`, which cannot be over-aligned.
     ///
     /// Note that these alignment requirements will vary between architectures
-    FixedWidth { byte_width: usize, alignment: usize },
+    FixedWidth {
+        /// The width of each element in bytes
+        byte_width: usize,
+        /// The alignment required by Rust for an array of the corresponding primitive
+        alignment: usize,
+    },
     /// Variable width, such as string data for utf8 data
     VariableWidth,
     /// Buffer holds a bitmap.
@@ -1783,6 +1789,7 @@ pub struct ArrayDataBuilder {
 
 impl ArrayDataBuilder {
     #[inline]
+    /// Creates a new array data builder
     pub const fn new(data_type: DataType) -> Self {
         Self {
             data_type,
@@ -1796,17 +1803,20 @@ impl ArrayDataBuilder {
         }
     }
 
+    /// Creates a new array data builder from an existing one, changing the data type
     pub fn data_type(self, data_type: DataType) -> Self {
         Self { data_type, ..self }
     }
 
     #[inline]
     #[allow(clippy::len_without_is_empty)]
+    /// Sets the length of the [ArrayData]
     pub const fn len(mut self, n: usize) -> Self {
         self.len = n;
         self
     }
 
+    /// Sets the null buffer of the [ArrayData]
     pub fn nulls(mut self, nulls: Option<NullBuffer>) -> Self {
         self.nulls = nulls;
         self.null_count = None;
@@ -1814,43 +1824,51 @@ impl ArrayDataBuilder {
         self
     }
 
+    /// Sets the null count of the [ArrayData]
     pub fn null_count(mut self, null_count: usize) -> Self {
         self.null_count = Some(null_count);
         self
     }
 
+    /// Sets the `null_bit_buffer` of the [ArrayData]
     pub fn null_bit_buffer(mut self, buf: Option<Buffer>) -> Self {
         self.nulls = None;
         self.null_bit_buffer = buf;
         self
     }
 
+    /// Sets the offset of the [ArrayData]
     #[inline]
     pub const fn offset(mut self, n: usize) -> Self {
         self.offset = n;
         self
     }
 
+    /// Sets the buffers of the [ArrayData]
     pub fn buffers(mut self, v: Vec<Buffer>) -> Self {
         self.buffers = v;
         self
     }
 
+    /// Adds a single buffer to the [ArrayData]'s buffers
     pub fn add_buffer(mut self, b: Buffer) -> Self {
         self.buffers.push(b);
         self
     }
 
-    pub fn add_buffers(mut self, bs: Vec<Buffer>) -> Self {
+    /// Adds multiple buffers to the [ArrayData]'s buffers
+    pub fn add_buffers<I: IntoIterator<Item = Buffer>>(mut self, bs: I) -> Self {
         self.buffers.extend(bs);
         self
     }
 
+    /// Sets the child data of the [ArrayData]
     pub fn child_data(mut self, v: Vec<ArrayData>) -> Self {
         self.child_data = v;
         self
     }
 
+    /// Adds a single child data to the [ArrayData]'s child data
     pub fn add_child_data(mut self, r: ArrayData) -> Self {
         self.child_data.push(r);
         self
@@ -1873,14 +1891,17 @@ impl ArrayDataBuilder {
 
     /// Same as [`Self::build_unchecked`] but ignoring `force_validate` feature flag
     unsafe fn build_impl(self) -> ArrayData {
-        let nulls = self.nulls.or_else(|| {
-            let buffer = self.null_bit_buffer?;
-            let buffer = BooleanBuffer::new(buffer, self.offset, self.len);
-            Some(match self.null_count {
-                Some(n) => NullBuffer::new_unchecked(buffer, n),
-                None => NullBuffer::new(buffer),
+        let nulls = self
+            .nulls
+            .or_else(|| {
+                let buffer = self.null_bit_buffer?;
+                let buffer = BooleanBuffer::new(buffer, self.offset, self.len);
+                Some(match self.null_count {
+                    Some(n) => NullBuffer::new_unchecked(buffer, n),
+                    None => NullBuffer::new(buffer),
+                })
             })
-        });
+            .filter(|b| b.null_count() != 0);
 
         ArrayData {
             data_type: self.data_type,
@@ -1888,7 +1909,7 @@ impl ArrayDataBuilder {
             offset: self.offset,
             buffers: self.buffers,
             child_data: self.child_data,
-            nulls: nulls.filter(|b| b.null_count() != 0),
+            nulls,
         }
     }
 
diff --git a/arrow-data/src/decimal.rs b/arrow-data/src/decimal.rs
index 74279bfb9..fe19db641 100644
--- a/arrow-data/src/decimal.rs
+++ b/arrow-data/src/decimal.rs
@@ -15,6 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Defines maximum and minimum values for `decimal256` and `decimal128` types for varying precisions.
+//!
+//! Also provides functions to validate if a given decimal value is within the valid range of the decimal type.
+
 use arrow_buffer::i256;
 use arrow_schema::ArrowError;
 
@@ -23,10 +27,13 @@ pub use arrow_schema::{
     DECIMAL_DEFAULT_SCALE,
 };
 
-// MAX decimal256 value of little-endian format for each precision.
-// Each element is the max value of signed 256-bit integer for the specified precision which
-// is encoded to the 32-byte width format of little-endian.
-pub(crate) const MAX_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION: [i256; 76] = [
+/// MAX decimal256 value of little-endian format for each precision.
+/// Each element is the max value of signed 256-bit integer for the specified precision which
+/// is encoded to the 32-byte width format of little-endian.
+/// The first element is unused and is inserted so that we can look up using
+/// precision as the index without the need to subtract 1 first.
+pub(crate) const MAX_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION: [i256; 77] = [
+    i256::from_i128(0_i128), // unused first element
     i256::from_le_bytes([
         9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0,
@@ -333,10 +340,13 @@ pub(crate) const MAX_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION: [i256; 76] = [
     ]),
 ];
 
-// MIN decimal256 value of little-endian format for each precision.
-// Each element is the min value of signed 256-bit integer for the specified precision which
-// is encoded to the 76-byte width format of little-endian.
-pub(crate) const MIN_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION: [i256; 76] = [
+/// MIN decimal256 value of little-endian format for each precision.
+/// Each element is the min value of signed 256-bit integer for the specified precision which
+/// is encoded to the 76-byte width format of little-endian.
+/// The first element is unused and is inserted so that we can look up using
+/// precision as the index without the need to subtract 1 first.
+pub(crate) const MIN_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION: [i256; 77] = [
+    i256::from_i128(0_i128), // unused first element
     i256::from_le_bytes([
         247, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
         255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@@ -643,8 +653,9 @@ pub(crate) const MIN_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION: [i256; 76] = [
     ]),
 ];
 
-/// `MAX_DECIMAL_FOR_EACH_PRECISION[p]` holds the maximum `i128` value that can
+/// `MAX_DECIMAL_FOR_EACH_PRECISION[p-1]` holds the maximum `i128` value that can
 /// be stored in [arrow_schema::DataType::Decimal128] value of precision `p`
+#[allow(dead_code)] // no longer used but is part of our public API
 pub const MAX_DECIMAL_FOR_EACH_PRECISION: [i128; 38] = [
     9,
     99,
@@ -686,8 +697,9 @@ pub const MAX_DECIMAL_FOR_EACH_PRECISION: [i128; 38] = [
     99999999999999999999999999999999999999,
 ];
 
-/// `MIN_DECIMAL_FOR_EACH_PRECISION[p]` holds the minimum `i128` value that can
+/// `MIN_DECIMAL_FOR_EACH_PRECISION[p-1]` holds the minimum `i128` value that can
 /// be stored in a [arrow_schema::DataType::Decimal128] value of precision `p`
+#[allow(dead_code)] // no longer used but is part of our public API
 pub const MIN_DECIMAL_FOR_EACH_PRECISION: [i128; 38] = [
     -9,
     -99,
@@ -729,6 +741,98 @@ pub const MIN_DECIMAL_FOR_EACH_PRECISION: [i128; 38] = [
     -99999999999999999999999999999999999999,
 ];
 
+/// `MAX_DECIMAL_FOR_EACH_PRECISION_ONE_BASED[p]` holds the maximum `i128` value that can
+/// be stored in [arrow_schema::DataType::Decimal128] value of precision `p`.
+/// The first element is unused and is inserted so that we can look up using
+/// precision as the index without the need to subtract 1 first.
+pub(crate) const MAX_DECIMAL_FOR_EACH_PRECISION_ONE_BASED: [i128; 39] = [
+    0, // unused first element
+    9,
+    99,
+    999,
+    9999,
+    99999,
+    999999,
+    9999999,
+    99999999,
+    999999999,
+    9999999999,
+    99999999999,
+    999999999999,
+    9999999999999,
+    99999999999999,
+    999999999999999,
+    9999999999999999,
+    99999999999999999,
+    999999999999999999,
+    9999999999999999999,
+    99999999999999999999,
+    999999999999999999999,
+    9999999999999999999999,
+    99999999999999999999999,
+    999999999999999999999999,
+    9999999999999999999999999,
+    99999999999999999999999999,
+    999999999999999999999999999,
+    9999999999999999999999999999,
+    99999999999999999999999999999,
+    999999999999999999999999999999,
+    9999999999999999999999999999999,
+    99999999999999999999999999999999,
+    999999999999999999999999999999999,
+    9999999999999999999999999999999999,
+    99999999999999999999999999999999999,
+    999999999999999999999999999999999999,
+    9999999999999999999999999999999999999,
+    99999999999999999999999999999999999999,
+];
+
+/// `MIN_DECIMAL_FOR_EACH_PRECISION[p]` holds the minimum `i128` value that can
+/// be stored in a [arrow_schema::DataType::Decimal128] value of precision `p`.
+/// The first element is unused and is inserted so that we can look up using
+/// precision as the index without the need to subtract 1 first.
+pub(crate) const MIN_DECIMAL_FOR_EACH_PRECISION_ONE_BASED: [i128; 39] = [
+    0, // unused first element
+    -9,
+    -99,
+    -999,
+    -9999,
+    -99999,
+    -999999,
+    -9999999,
+    -99999999,
+    -999999999,
+    -9999999999,
+    -99999999999,
+    -999999999999,
+    -9999999999999,
+    -99999999999999,
+    -999999999999999,
+    -9999999999999999,
+    -99999999999999999,
+    -999999999999999999,
+    -9999999999999999999,
+    -99999999999999999999,
+    -999999999999999999999,
+    -9999999999999999999999,
+    -99999999999999999999999,
+    -999999999999999999999999,
+    -9999999999999999999999999,
+    -99999999999999999999999999,
+    -999999999999999999999999999,
+    -9999999999999999999999999999,
+    -99999999999999999999999999999,
+    -999999999999999999999999999999,
+    -9999999999999999999999999999999,
+    -99999999999999999999999999999999,
+    -999999999999999999999999999999999,
+    -9999999999999999999999999999999999,
+    -99999999999999999999999999999999999,
+    -999999999999999999999999999999999999,
+    -9999999999999999999999999999999999999,
+    -99999999999999999999999999999999999999,
+];
+
 /// Validates that the specified `i128` value can be properly
 /// interpreted as a Decimal number with precision `precision`
 #[inline]
@@ -738,23 +842,30 @@ pub fn validate_decimal_precision(value: i128, precision: u8) -> Result<(), Arro
             "Max precision of a Decimal128 is {DECIMAL128_MAX_PRECISION}, but got {precision}",
         )));
     }
-
-    let max = MAX_DECIMAL_FOR_EACH_PRECISION[usize::from(precision) - 1];
-    let min = MIN_DECIMAL_FOR_EACH_PRECISION[usize::from(precision) - 1];
-
-    if value > max {
+    if value > MAX_DECIMAL_FOR_EACH_PRECISION_ONE_BASED[precision as usize] {
         Err(ArrowError::InvalidArgumentError(format!(
-            "{value} is too large to store in a Decimal128 of precision {precision}. Max is {max}"
+            "{value} is too large to store in a Decimal128 of precision {precision}. Max is {}",
+            MAX_DECIMAL_FOR_EACH_PRECISION_ONE_BASED[precision as usize]
         )))
-    } else if value < min {
+    } else if value < MIN_DECIMAL_FOR_EACH_PRECISION_ONE_BASED[precision as usize] {
         Err(ArrowError::InvalidArgumentError(format!(
-            "{value} is too small to store in a Decimal128 of precision {precision}. Min is {min}"
+            "{value} is too small to store in a Decimal128 of precision {precision}. Min is {}",
+            MIN_DECIMAL_FOR_EACH_PRECISION_ONE_BASED[precision as usize]
         )))
     } else {
         Ok(())
     }
 }
 
+/// Determines whether the specified `i128` value can be properly
+/// interpreted as a Decimal number with precision `precision`
+#[inline]
+pub fn is_validate_decimal_precision(value: i128, precision: u8) -> bool {
+    precision <= DECIMAL128_MAX_PRECISION
+        && value >= MIN_DECIMAL_FOR_EACH_PRECISION_ONE_BASED[precision as usize]
+        && value <= MAX_DECIMAL_FOR_EACH_PRECISION_ONE_BASED[precision as usize]
+}
+
 /// Validates that the specified `i256` of value can be properly
 /// interpreted as a Decimal256 number with precision `precision`
 #[inline]
@@ -764,18 +875,26 @@ pub fn validate_decimal256_precision(value: i256, precision: u8) -> Result<(), A
             "Max precision of a Decimal256 is {DECIMAL256_MAX_PRECISION}, but got {precision}",
         )));
     }
-    let max = MAX_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[usize::from(precision) - 1];
-    let min = MIN_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[usize::from(precision) - 1];
-
-    if value > max {
+    if value > MAX_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[precision as usize] {
         Err(ArrowError::InvalidArgumentError(format!(
-            "{value:?} is too large to store in a Decimal256 of precision {precision}. Max is {max:?}"
+            "{value:?} is too large to store in a Decimal256 of precision {precision}. Max is {:?}",
+            MAX_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[precision as usize]
         )))
-    } else if value < min {
+    } else if value < MIN_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[precision as usize] {
         Err(ArrowError::InvalidArgumentError(format!(
-            "{value:?} is too small to store in a Decimal256 of precision {precision}. Min is {min:?}"
+            "{value:?} is too small to store in a Decimal256 of precision {precision}. Min is {:?}",
+            MIN_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[precision as usize]
         )))
     } else {
         Ok(())
     }
 }
+
+/// Determines whether the specified `i256` value can be properly
+/// interpreted as a Decimal256 number with precision `precision`
+#[inline]
+pub fn is_validate_decimal256_precision(value: i256, precision: u8) -> bool {
+    precision <= DECIMAL256_MAX_PRECISION
+        && value >= MIN_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[precision as usize]
+        && value <= MAX_DECIMAL_BYTES_FOR_LARGER_EACH_PRECISION[precision as usize]
+}
diff --git a/arrow-data/src/lib.rs b/arrow-data/src/lib.rs
index 59a049fe9..a7feca6cd 100644
--- a/arrow-data/src/lib.rs
+++ b/arrow-data/src/lib.rs
@@ -19,6 +19,7 @@
 //!
 //! For a higher-level, strongly-typed interface see [arrow_array](https://docs.rs/arrow_array)
 
+#![warn(missing_docs)]
 mod data;
 pub use data::*;
 
diff --git a/arrow-data/src/transform/mod.rs b/arrow-data/src/transform/mod.rs
index edc68f1a1..c74b0c434 100644
--- a/arrow-data/src/transform/mod.rs
+++ b/arrow-data/src/transform/mod.rs
@@ -15,6 +15,11 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Low-level array data abstractions.
+//!
+//! Provides utilities for creating, manipulating, and converting Arrow arrays
+//! made of primitive types, strings, and nested types.
+
 use super::{data::new_buffers, ArrayData, ArrayDataBuilder, ByteView};
 use crate::bit_mask::set_bits;
 use arrow_buffer::buffer::{BooleanBuffer, NullBuffer};
diff --git a/arrow-flight/Cargo.toml b/arrow-flight/Cargo.toml
index be0b1dccc..64bf2041e 100644
--- a/arrow-flight/Cargo.toml
+++ b/arrow-flight/Cargo.toml
@@ -20,7 +20,7 @@ name = "arrow-flight"
 description = "Apache Arrow Flight"
 version = { workspace = true }
 edition = { workspace = true }
-rust-version = "1.70.0"
+rust-version = "1.71.1"
 authors = { workspace = true }
 homepage = { workspace = true }
 repository = { workspace = true }
diff --git a/arrow-json/src/reader/mod.rs b/arrow-json/src/reader/mod.rs
index 97d9c8962..bcacf6f70 100644
--- a/arrow-json/src/reader/mod.rs
+++ b/arrow-json/src/reader/mod.rs
@@ -1007,7 +1007,7 @@ mod tests {
         let map_values = map.values().as_list::<i32>();
         assert_eq!(map.value_offsets(), &[0, 1, 3, 5]);
 
-        let k: Vec<_> = map_keys.iter().map(|x| x.unwrap()).collect();
+        let k: Vec<_> = map_keys.iter().flatten().collect();
         assert_eq!(&k, &["a", "a", "b", "c", "a"]);
 
         let list_values = map_values.values().as_string::<i32>();
diff --git a/arrow-ord/src/lib.rs b/arrow-ord/src/lib.rs
index 8fe4ecbc0..93fcb98a0 100644
--- a/arrow-ord/src/lib.rs
+++ b/arrow-ord/src/lib.rs
@@ -43,6 +43,7 @@
 //! ```
 //!
 
+#![warn(missing_docs)]
 pub mod cmp;
 #[doc(hidden)]
 pub mod comparison;
diff --git a/arrow-ord/src/rank.rs b/arrow-ord/src/rank.rs
index 51b0b5b91..ecc693bab 100644
--- a/arrow-ord/src/rank.rs
+++ b/arrow-ord/src/rank.rs
@@ -15,6 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Provides `rank` function to assign a rank to each value in an array
+
 use arrow_array::cast::AsArray;
 use arrow_array::types::*;
 use arrow_array::{downcast_primitive_array, Array, ArrowNativeTypeOp, GenericByteArray};
diff --git a/arrow-ord/src/sort.rs b/arrow-ord/src/sort.rs
index 168f82747..60fc4a918 100644
--- a/arrow-ord/src/sort.rs
+++ b/arrow-ord/src/sort.rs
@@ -635,7 +635,9 @@ where
 /// One column to be used in lexicographical sort
 #[derive(Clone, Debug)]
 pub struct SortColumn {
+    /// The column to sort
     pub values: ArrayRef,
+    /// Sort options for this column
     pub options: Option<SortOptions>,
 }
 
diff --git a/arrow-row/src/lib.rs b/arrow-row/src/lib.rs
index 2d9af7575..ac859e90e 100644
--- a/arrow-row/src/lib.rs
+++ b/arrow-row/src/lib.rs
@@ -125,6 +125,7 @@
 //! [compared]: PartialOrd
 //! [compare]: PartialOrd
 
+#![warn(missing_docs)]
 use std::cmp::Ordering;
 use std::hash::{Hash, Hasher};
 use std::sync::Arc;
diff --git a/arrow-schema/src/datatype.rs b/arrow-schema/src/datatype.rs
index b9cfc3d8a..32eb95aef 100644
--- a/arrow-schema/src/datatype.rs
+++ b/arrow-schema/src/datatype.rs
@@ -420,11 +420,13 @@ pub enum IntervalUnit {
     MonthDayNano,
 }
 
-// Sparse or Dense union layouts
+/// Sparse or Dense union layouts
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 pub enum UnionMode {
+    /// Sparse union layout
     Sparse,
+    /// Dense union layout
     Dense,
 }
 
diff --git a/arrow-schema/src/error.rs b/arrow-schema/src/error.rs
index 5e632d051..982dd026a 100644
--- a/arrow-schema/src/error.rs
+++ b/arrow-schema/src/error.rs
@@ -26,23 +26,39 @@ use std::error::Error;
 pub enum ArrowError {
     /// Returned when functionality is not yet available.
     NotYetImplemented(String),
+    /// Wraps an external error.
     ExternalError(Box<dyn Error + Send + Sync>),
+    /// Error during casting from one type to another.
     CastError(String),
+    /// Memory or buffer error.
     MemoryError(String),
+    /// Error during parsing from a string.
     ParseError(String),
+    /// Error during schema-related operations.
     SchemaError(String),
+    /// Error during computation.
     ComputeError(String),
+    /// Error during division by zero.
     DivideByZero,
+    /// Error when an arithmetic operation overflows.
     ArithmeticOverflow(String),
+    /// Error during CSV-related operations.
     CsvError(String),
+    /// Error during JSON-related operations.
     JsonError(String),
+    /// Error during IO operations.
     IoError(String, std::io::Error),
+    /// Error during IPC operations in `arrow-ipc` or `arrow-flight`.
     IpcError(String),
+    /// Error indicating that an unexpected or bad argument was passed to a function.
     InvalidArgumentError(String),
+    /// Error during Parquet operations.
     ParquetError(String),
     /// Error during import or export to/from the C Data Interface
     CDataInterface(String),
+    /// Error when a dictionary key is bigger than the key type
     DictionaryKeyOverflowError,
+    /// Error when the run end index in a REE array is bigger than the array length
     RunEndIndexOverflowError,
 }
 
diff --git a/arrow-schema/src/ffi.rs b/arrow-schema/src/ffi.rs
index c68849059..e12c37da4 100644
--- a/arrow-schema/src/ffi.rs
+++ b/arrow-schema/src/ffi.rs
@@ -37,25 +37,27 @@
 use crate::{
     ArrowError, DataType, Field, FieldRef, IntervalUnit, Schema, TimeUnit, UnionFields, UnionMode,
 };
+use bitflags::bitflags;
 use std::sync::Arc;
 use std::{
     collections::HashMap,
     ffi::{c_char, c_void, CStr, CString},
 };
 
-#[allow(clippy::assign_op_pattern)]
-/// Workaround <https://github.com/bitflags/bitflags/issues/356>
-mod flags {
-    use bitflags::bitflags;
-    bitflags! {
-        pub struct Flags: i64 {
-            const DICTIONARY_ORDERED = 0b00000001;
-            const NULLABLE = 0b00000010;
-            const MAP_KEYS_SORTED = 0b00000100;
-        }
+bitflags! {
+    /// Flags for [`FFI_ArrowSchema`]
+    ///
+    /// Old Workaround at <https://github.com/bitflags/bitflags/issues/356>
+    /// is no longer required as `bitflags` [fixed the issue](https://github.com/bitflags/bitflags/pull/355).
+    pub struct Flags: i64 {
+        /// Indicates that the dictionary is ordered
+        const DICTIONARY_ORDERED = 0b00000001;
+        /// Indicates that the field is nullable
+        const NULLABLE = 0b00000010;
+        /// Indicates that the map keys are sorted
+        const MAP_KEYS_SORTED = 0b00000100;
     }
 }
-pub use flags::*;
 
 /// ABI-compatible struct for `ArrowSchema` from C Data Interface
 /// See <https://arrow.apache.org/docs/format/CDataInterface.html#structure-definitions>
@@ -70,10 +72,12 @@ pub use flags::*;
 ///
 #[repr(C)]
 #[derive(Debug)]
+#[allow(non_camel_case_types)]
 pub struct FFI_ArrowSchema {
     format: *const c_char,
     name: *const c_char,
     metadata: *const c_char,
+    /// Refer to [Arrow Flags](https://arrow.apache.org/docs/format/CDataInterface.html#c.ArrowSchema.flags)
     flags: i64,
     n_children: i64,
     children: *mut *mut FFI_ArrowSchema,
@@ -155,16 +159,19 @@ impl FFI_ArrowSchema {
         Ok(this)
     }
 
+    /// Set the name of the schema
     pub fn with_name(mut self, name: &str) -> Result<Self, ArrowError> {
         self.name = CString::new(name).unwrap().into_raw();
         Ok(self)
     }
 
+    /// Set the flags of the schema
     pub fn with_flags(mut self, flags: Flags) -> Result<Self, ArrowError> {
         self.flags = flags.bits();
         Ok(self)
     }
 
+    /// Add metadata to the schema
     pub fn with_metadata<I, S>(mut self, metadata: I) -> Result<Self, ArrowError>
     where
         I: IntoIterator<Item = (S, S)>,
@@ -237,6 +244,7 @@ impl FFI_ArrowSchema {
         std::ptr::replace(schema, Self::empty())
     }
 
+    /// Create an empty [`FFI_ArrowSchema`]
     pub fn empty() -> Self {
         Self {
             format: std::ptr::null_mut(),
@@ -251,7 +259,7 @@ impl FFI_ArrowSchema {
         }
     }
 
-    /// returns the format of this schema.
+    /// Returns the format of this schema.
     pub fn format(&self) -> &str {
         assert!(!self.format.is_null());
         // safe because the lifetime of `self.format` equals `self`
@@ -260,7 +268,7 @@ impl FFI_ArrowSchema {
             .expect("The external API has a non-utf8 as format")
     }
 
-    /// returns the name of this schema.
+    /// Returns the name of this schema.
     pub fn name(&self) -> Option<&str> {
         if self.name.is_null() {
             None
@@ -274,35 +282,55 @@ impl FFI_ArrowSchema {
         }
     }
 
+    /// Returns the flags of this schema.
     pub fn flags(&self) -> Option<Flags> {
         Flags::from_bits(self.flags)
     }
 
+    /// Returns the child of this schema at `index`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `index` is greater than or equal to the number of children.
+    ///
+    /// This is to make sure that the unsafe acces to raw pointer is sound.
     pub fn child(&self, index: usize) -> &Self {
         assert!(index < self.n_children as usize);
         unsafe { self.children.add(index).as_ref().unwrap().as_ref().unwrap() }
     }
 
+    /// Returns an iterator to the schema's children.
     pub fn children(&self) -> impl Iterator<Item = &Self> {
         (0..self.n_children as usize).map(move |i| self.child(i))
     }
 
+    /// Returns if the field is semantically nullable,
+    /// regardless of whether it actually has null values.
     pub fn nullable(&self) -> bool {
         (self.flags / 2) & 1 == 1
     }
 
+    /// Returns the reference to the underlying dictionary of the schema.
+    /// Check [ArrowSchema.dictionary](https://arrow.apache.org/docs/format/CDataInterface.html#c.ArrowSchema.dictionary).
+    ///
+    /// This must be `Some` if the schema represents a dictionary-encoded type, `None` otherwise.
     pub fn dictionary(&self) -> Option<&Self> {
         unsafe { self.dictionary.as_ref() }
     }
 
+    /// For map types, returns whether the keys within each map value are sorted.
+    ///
+    /// Refer to [Arrow Flags](https://arrow.apache.org/docs/format/CDataInterface.html#c.ArrowSchema.flags)
     pub fn map_keys_sorted(&self) -> bool {
         self.flags & 0b00000100 != 0
     }
 
+    /// For dictionary-encoded types, returns whether the ordering of dictionary indices is semantically meaningful.
     pub fn dictionary_ordered(&self) -> bool {
         self.flags & 0b00000001 != 0
     }
 
+    /// Returns the metadata in the schema as `Key-Value` pairs
     pub fn metadata(&self) -> Result<HashMap<String, String>, ArrowError> {
         if self.metadata.is_null() {
             Ok(HashMap::new())
diff --git a/arrow-schema/src/lib.rs b/arrow-schema/src/lib.rs
index 27466593d..d513ca7dd 100644
--- a/arrow-schema/src/lib.rs
+++ b/arrow-schema/src/lib.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+#![warn(missing_docs)]
 //! Arrow logical types
 
 mod datatype;
diff --git a/arrow-schema/src/schema.rs b/arrow-schema/src/schema.rs
index 9a9ef45d8..cc3a8a308 100644
--- a/arrow-schema/src/schema.rs
+++ b/arrow-schema/src/schema.rs
@@ -185,6 +185,7 @@ pub type SchemaRef = Arc<Schema>;
 #[derive(Debug, Clone, PartialEq, Eq)]
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 pub struct Schema {
+    /// A sequence of fields that describe the schema.
     pub fields: Fields,
     /// A map of key-value pairs containing additional meta data.
     pub metadata: HashMap<String, String>,
diff --git a/arrow-select/src/filter.rs b/arrow-select/src/filter.rs
index e59ad50dd..8198980cb 100644
--- a/arrow-select/src/filter.rs
+++ b/arrow-select/src/filter.rs
@@ -56,6 +56,7 @@ const FILTER_SLICES_SELECTIVITY_THRESHOLD: f64 = 0.8;
 pub struct SlicesIterator<'a>(BitSliceIterator<'a>);
 
 impl<'a> SlicesIterator<'a> {
+    /// Creates a new iterator from a [BooleanArray]
     pub fn new(filter: &'a BooleanArray) -> Self {
         Self(filter.values().set_slices())
     }
diff --git a/arrow-select/src/interleave.rs b/arrow-select/src/interleave.rs
index a2ab7ba63..a0520e969 100644
--- a/arrow-select/src/interleave.rs
+++ b/arrow-select/src/interleave.rs
@@ -15,6 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Interleave elements from multiple arrays
+
 use crate::dictionary::{merge_dictionary_values, should_merge_dictionary_values};
 use arrow_array::builder::{BooleanBufferBuilder, BufferBuilder, PrimitiveBuilder};
 use arrow_array::cast::AsArray;
diff --git a/arrow-select/src/lib.rs b/arrow-select/src/lib.rs
index 82f57a6af..b79682171 100644
--- a/arrow-select/src/lib.rs
+++ b/arrow-select/src/lib.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+#![warn(missing_docs)]
 //! Arrow selection kernels
 
 pub mod concat;
diff --git a/arrow-select/src/nullif.rs b/arrow-select/src/nullif.rs
index a7848c16a..d1e3c35bf 100644
--- a/arrow-select/src/nullif.rs
+++ b/arrow-select/src/nullif.rs
@@ -15,6 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Implements the `nullif` function for Arrow arrays.
+
 use arrow_array::{make_array, Array, ArrayRef, BooleanArray};
 use arrow_buffer::buffer::{bitwise_bin_op_helper, bitwise_unary_op_helper};
 use arrow_buffer::{BooleanBuffer, NullBuffer};
diff --git a/arrow-select/src/zip.rs b/arrow-select/src/zip.rs
index ff2380ef2..1f317dfd4 100644
--- a/arrow-select/src/zip.rs
+++ b/arrow-select/src/zip.rs
@@ -15,6 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Zip two arrays by some boolean mask. Where the mask evaluates `true` values of `truthy`
+
 use crate::filter::SlicesIterator;
 use arrow_array::*;
 use arrow_data::transform::MutableArrayData;
diff --git a/arrow-string/src/concat_elements.rs b/arrow-string/src/concat_elements.rs
index cb60363d3..41be8a81c 100644
--- a/arrow-string/src/concat_elements.rs
+++ b/arrow-string/src/concat_elements.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Provides utility functions for concatenation of elements in arrays.
 use std::sync::Arc;
 
 use arrow_array::builder::BufferBuilder;
@@ -167,6 +168,11 @@ pub fn concat_elements_utf8_many<Offset: OffsetSizeTrait>(
     Ok(unsafe { builder.build_unchecked() }.into())
 }
 
+/// Returns the elementwise concatenation of [`Array`]s.
+///
+/// # Errors
+///
+/// This function errors if the arrays are of different types.
 pub fn concat_elements_dyn(left: &dyn Array, right: &dyn Array) -> Result<ArrayRef, ArrowError> {
     if left.data_type() != right.data_type() {
         return Err(ArrowError::ComputeError(format!(
diff --git a/arrow-string/src/lib.rs b/arrow-string/src/lib.rs
index 4444b37a7..87e23a7b2 100644
--- a/arrow-string/src/lib.rs
+++ b/arrow-string/src/lib.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+#![warn(missing_docs)]
 //! Arrow string kernels
 
 pub mod concat_elements;
diff --git a/arrow-string/src/like.rs b/arrow-string/src/like.rs
index 4626be136..6d82682d7 100644
--- a/arrow-string/src/like.rs
+++ b/arrow-string/src/like.rs
@@ -15,6 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! Provide SQL's LIKE operators for Arrow's string arrays
+
 use crate::predicate::Predicate;
 use arrow_array::cast::AsArray;
 use arrow_array::*;
@@ -155,7 +157,7 @@ fn like_op(op: Op, lhs: &dyn Datum, rhs: &dyn Datum) -> Result<BooleanArray, Arr
 ///
 /// This trait helps to abstract over the different types of string arrays
 /// so that we don't need to duplicate the implementation for each type.
-trait StringArrayType<'a>: ArrayAccessor<Item = &'a str> + Sized {
+pub trait StringArrayType<'a>: ArrayAccessor<Item = &'a str> + Sized {
     fn is_ascii(&self) -> bool;
     fn iter(&self) -> ArrayIter<Self>;
 }
diff --git a/arrow-string/src/regexp.rs b/arrow-string/src/regexp.rs
index f79eff4b6..5ad452a17 100644
--- a/arrow-string/src/regexp.rs
+++ b/arrow-string/src/regexp.rs
@@ -18,6 +18,8 @@
 //! Defines kernel to extract substrings based on a regular
 //! expression of a \[Large\]StringArray
 
+use crate::like::StringArrayType;
+
 use arrow_array::builder::{BooleanBufferBuilder, GenericStringBuilder, ListBuilder};
 use arrow_array::cast::AsArray;
 use arrow_array::*;
@@ -25,6 +27,7 @@ use arrow_buffer::NullBuffer;
 use arrow_data::{ArrayData, ArrayDataBuilder};
 use arrow_schema::{ArrowError, DataType, Field};
 use regex::Regex;
+
 use std::collections::HashMap;
 use std::sync::Arc;
 
@@ -35,16 +38,64 @@ use std::sync::Arc;
 /// special search modes, such as case insensitive and multi-line mode.
 /// See the documentation [here](https://docs.rs/regex/1.5.4/regex/#grouping-and-flags)
 /// for more information.
+#[deprecated(since = "54.0.0", note = "please use `regex_is_match` instead")]
 pub fn regexp_is_match_utf8<OffsetSize: OffsetSizeTrait>(
     array: &GenericStringArray<OffsetSize>,
     regex_array: &GenericStringArray<OffsetSize>,
     flags_array: Option<&GenericStringArray<OffsetSize>>,
 ) -> Result<BooleanArray, ArrowError> {
+    regexp_is_match(array, regex_array, flags_array)
+}
+
+/// Return BooleanArray indicating which strings in an array match an array of
+/// regular expressions.
+///
+/// This is equivalent to the SQL `array ~ regex_array`, supporting
+/// [`StringArray`] / [`LargeStringArray`] / [`StringViewArray`].
+///
+/// If `regex_array` element has an empty value, the corresponding result value is always true.
+///
+/// `flags_array` are optional [`StringArray`] / [`LargeStringArray`] / [`StringViewArray`] flag,
+/// which allow special search modes, such as case-insensitive and multi-line mode.
+/// See the documentation [here](https://docs.rs/regex/1.5.4/regex/#grouping-and-flags)
+/// for more information.
+///
+/// # See Also
+/// * [`regexp_is_match_scalar`] for matching a single regular expression against an array of strings
+/// * [`regexp_match`] for extracting groups from a string array based on a regular expression
+///
+/// # Example
+/// ```
+/// # use arrow_array::{StringArray, BooleanArray};
+/// # use arrow_string::regexp::regexp_is_match;
+/// // First array is the array of strings to match
+/// let array = StringArray::from(vec!["Foo", "Bar", "FooBar", "Baz"]);
+/// // Second array is the array of regular expressions to match against
+/// let regex_array = StringArray::from(vec!["^Foo", "^Foo", "Bar$", "Baz"]);
+/// // Third array is the array of flags to use for each regular expression, if desired
+/// // (the type must be provided to satisfy type inference for the third parameter)
+/// let flags_array: Option<&StringArray> = None;
+/// // The result is a BooleanArray indicating when each string in `array`
+/// // matches the corresponding regular expression in `regex_array`
+/// let result = regexp_is_match(&array, &regex_array, flags_array).unwrap();
+/// assert_eq!(result, BooleanArray::from(vec![true, false, true, true]));
+/// ```
+pub fn regexp_is_match<'a, S1, S2, S3>(
+    array: &'a S1,
+    regex_array: &'a S2,
+    flags_array: Option<&'a S3>,
+) -> Result<BooleanArray, ArrowError>
+where
+    &'a S1: StringArrayType<'a>,
+    &'a S2: StringArrayType<'a>,
+    &'a S3: StringArrayType<'a>,
+{
     if array.len() != regex_array.len() {
         return Err(ArrowError::ComputeError(
             "Cannot perform comparison operation on arrays of different length".to_string(),
         ));
     }
+
     let nulls = NullBuffer::union(array.nulls(), regex_array.nulls());
 
     let mut patterns: HashMap<String, Regex> = HashMap::new();
@@ -107,6 +158,7 @@ pub fn regexp_is_match_utf8<OffsetSize: OffsetSizeTrait>(
             .nulls(nulls)
             .build_unchecked()
     };
+
     Ok(BooleanArray::from(data))
 }
 
@@ -114,11 +166,47 @@ pub fn regexp_is_match_utf8<OffsetSize: OffsetSizeTrait>(
 /// [`LargeStringArray`] and a scalar.
 ///
 /// See the documentation on [`regexp_is_match_utf8`] for more details.
+#[deprecated(since = "54.0.0", note = "please use `regex_is_match_scalar` instead")]
 pub fn regexp_is_match_utf8_scalar<OffsetSize: OffsetSizeTrait>(
     array: &GenericStringArray<OffsetSize>,
     regex: &str,
     flag: Option<&str>,
 ) -> Result<BooleanArray, ArrowError> {
+    regexp_is_match_scalar(array, regex, flag)
+}
+
+/// Return BooleanArray indicating which strings in an array match a single regular expression.
+///
+/// This is equivalent to the SQL `array ~ regex_array`, supporting
+/// [`StringArray`] / [`LargeStringArray`] / [`StringViewArray`] and a scalar.
+///
+/// See the documentation on [`regexp_is_match`] for more details on arguments
+///
+/// # See Also
+/// * [`regexp_is_match`] for matching an array of regular expression against an array of strings
+/// * [`regexp_match`] for extracting groups from a string array based on a regular expression
+///
+/// # Example
+/// ```
+/// # use arrow_array::{StringArray, BooleanArray};
+/// # use arrow_string::regexp::regexp_is_match_scalar;
+/// // array of strings to match
+/// let array = StringArray::from(vec!["Foo", "Bar", "FooBar", "Baz"]);
+/// let regexp = "^Foo"; // regular expression to match against
+/// let flags: Option<&str> = None;  // flags can control the matching behavior
+/// // The result is a BooleanArray indicating when each string in `array`
+/// // matches the regular expression `regexp`
+/// let result = regexp_is_match_scalar(&array, regexp, None).unwrap();
+/// assert_eq!(result, BooleanArray::from(vec![true, false, true, false]));
+/// ```
+pub fn regexp_is_match_scalar<'a, S>(
+    array: &'a S,
+    regex: &str,
+    flag: Option<&str>,
+) -> Result<BooleanArray, ArrowError>
+where
+    &'a S: StringArrayType<'a>,
+{
     let null_bit_buffer = array.nulls().map(|x| x.inner().sliced());
     let mut result = BooleanBufferBuilder::new(array.len());
 
@@ -126,6 +214,7 @@ pub fn regexp_is_match_utf8_scalar<OffsetSize: OffsetSizeTrait>(
         Some(flag) => format!("(?{flag}){regex}"),
         None => regex.to_string(),
     };
+
     if pattern.is_empty() {
         result.append_n(array.len(), true);
     } else {
@@ -150,6 +239,7 @@ pub fn regexp_is_match_utf8_scalar<OffsetSize: OffsetSizeTrait>(
             vec![],
         )
     };
+
     Ok(BooleanArray::from(data))
 }
 
@@ -303,6 +393,9 @@ fn regexp_scalar_match<OffsetSize: OffsetSizeTrait>(
 /// The flags parameter is an optional text string containing zero or more single-letter flags
 /// that change the function's behavior.
 ///
+/// # See Also
+/// * [`regexp_is_match`] for matching (rather than extracting) a regular expression against an array of strings
+///
 /// [regexp_match]: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-POSIX-REGEXP
 pub fn regexp_match(
     array: &dyn Array,
@@ -517,8 +610,8 @@ mod tests {
         ($test_name:ident, $left:expr, $right:expr, $op:expr, $expected:expr) => {
             #[test]
             fn $test_name() {
-                let left = StringArray::from($left);
-                let right = StringArray::from($right);
+                let left = $left;
+                let right = $right;
                 let res = $op(&left, &right, None).unwrap();
                 let expected = $expected;
                 assert_eq!(expected.len(), res.len());
@@ -531,9 +624,9 @@ mod tests {
         ($test_name:ident, $left:expr, $right:expr, $flag:expr, $op:expr, $expected:expr) => {
             #[test]
             fn $test_name() {
-                let left = StringArray::from($left);
-                let right = StringArray::from($right);
-                let flag = Some(StringArray::from($flag));
+                let left = $left;
+                let right = $right;
+                let flag = Some($flag);
                 let res = $op(&left, &right, flag.as_ref()).unwrap();
                 let expected = $expected;
                 assert_eq!(expected.len(), res.len());
@@ -549,7 +642,7 @@ mod tests {
         ($test_name:ident, $left:expr, $right:expr, $op:expr, $expected:expr) => {
             #[test]
             fn $test_name() {
-                let left = StringArray::from($left);
+                let left = $left;
                 let res = $op(&left, $right, None).unwrap();
                 let expected = $expected;
                 assert_eq!(expected.len(), res.len());
@@ -569,7 +662,7 @@ mod tests {
         ($test_name:ident, $left:expr, $right:expr, $flag:expr, $op:expr, $expected:expr) => {
             #[test]
             fn $test_name() {
-                let left = StringArray::from($left);
+                let left = $left;
                 let flag = Some($flag);
                 let res = $op(&left, $right, flag).unwrap();
                 let expected = $expected;
@@ -590,41 +683,126 @@ mod tests {
     }
 
     test_flag_utf8!(
-        test_utf8_array_regexp_is_match,
-        vec!["arrow", "arrow", "arrow", "arrow", "arrow", "arrow"],
-        vec!["^ar", "^AR", "ow$", "OW$", "foo", ""],
-        regexp_is_match_utf8,
+        test_array_regexp_is_match_utf8,
+        StringArray::from(vec!["arrow", "arrow", "arrow", "arrow", "arrow", "arrow"]),
+        StringArray::from(vec!["^ar", "^AR", "ow$", "OW$", "foo", ""]),
+        regexp_is_match::<StringArray, StringArray, StringArray>,
         [true, false, true, false, false, true]
     );
     test_flag_utf8!(
-        test_utf8_array_regexp_is_match_insensitive,
-        vec!["arrow", "arrow", "arrow", "arrow", "arrow", "arrow"],
-        vec!["^ar", "^AR", "ow$", "OW$", "foo", ""],
-        vec!["i"; 6],
-        regexp_is_match_utf8,
+        test_array_regexp_is_match_utf8_insensitive,
+        StringArray::from(vec!["arrow", "arrow", "arrow", "arrow", "arrow", "arrow"]),
+        StringArray::from(vec!["^ar", "^AR", "ow$", "OW$", "foo", ""]),
+        StringArray::from(vec!["i"; 6]),
+        regexp_is_match,
         [true, true, true, true, false, true]
     );
 
     test_flag_utf8_scalar!(
-        test_utf8_array_regexp_is_match_scalar,
-        vec!["arrow", "ARROW", "parquet", "PARQUET"],
+        test_array_regexp_is_match_utf8_scalar,
+        StringArray::from(vec!["arrow", "ARROW", "parquet", "PARQUET"]),
         "^ar",
-        regexp_is_match_utf8_scalar,
+        regexp_is_match_scalar,
         [true, false, false, false]
     );
     test_flag_utf8_scalar!(
-        test_utf8_array_regexp_is_match_empty_scalar,
-        vec!["arrow", "ARROW", "parquet", "PARQUET"],
+        test_array_regexp_is_match_utf8_scalar_empty,
+        StringArray::from(vec!["arrow", "ARROW", "parquet", "PARQUET"]),
         "",
-        regexp_is_match_utf8_scalar,
+        regexp_is_match_scalar,
         [true, true, true, true]
     );
     test_flag_utf8_scalar!(
-        test_utf8_array_regexp_is_match_insensitive_scalar,
-        vec!["arrow", "ARROW", "parquet", "PARQUET"],
+        test_array_regexp_is_match_utf8_scalar_insensitive,
+        StringArray::from(vec!["arrow", "ARROW", "parquet", "PARQUET"]),
         "^ar",
         "i",
-        regexp_is_match_utf8_scalar,
+        regexp_is_match_scalar,
+        [true, true, false, false]
+    );
+
+    test_flag_utf8!(
+        tes_array_regexp_is_match,
+        StringViewArray::from(vec!["arrow", "arrow", "arrow", "arrow", "arrow", "arrow"]),
+        StringViewArray::from(vec!["^ar", "^AR", "ow$", "OW$", "foo", ""]),
+        regexp_is_match::<StringViewArray, StringViewArray, StringViewArray>,
+        [true, false, true, false, false, true]
+    );
+    test_flag_utf8!(
+        test_array_regexp_is_match_2,
+        StringViewArray::from(vec!["arrow", "arrow", "arrow", "arrow", "arrow", "arrow"]),
+        StringArray::from(vec!["^ar", "^AR", "ow$", "OW$", "foo", ""]),
+        regexp_is_match::<StringViewArray, GenericStringArray<i32>, GenericStringArray<i32>>,
+        [true, false, true, false, false, true]
+    );
+    test_flag_utf8!(
+        test_array_regexp_is_match_insensitive,
+        StringViewArray::from(vec![
+            "Official Rust implementation of Apache Arrow",
+            "apache/arrow-rs",
+            "apache/arrow-rs",
+            "parquet",
+            "parquet",
+            "row",
+            "row",
+        ]),
+        StringViewArray::from(vec![
+            ".*rust implement.*",
+            "^ap",
+            "^AP",
+            "et$",
+            "ET$",
+            "foo",
+            ""
+        ]),
+        StringViewArray::from(vec!["i"; 7]),
+        regexp_is_match::<StringViewArray, StringViewArray, StringViewArray>,
+        [true, true, true, true, true, false, true]
+    );
+    test_flag_utf8!(
+        test_array_regexp_is_match_insensitive_2,
+        LargeStringArray::from(vec!["arrow", "arrow", "arrow", "arrow", "arrow", "arrow"]),
+        StringViewArray::from(vec!["^ar", "^AR", "ow$", "OW$", "foo", ""]),
+        StringArray::from(vec!["i"; 6]),
+        regexp_is_match::<GenericStringArray<i64>, StringViewArray, GenericStringArray<i32>>,
+        [true, true, true, true, false, true]
+    );
+
+    test_flag_utf8_scalar!(
+        test_array_regexp_is_match_scalar,
+        StringViewArray::from(vec![
+            "apache/arrow-rs",
+            "APACHE/ARROW-RS",
+            "parquet",
+            "PARQUET",
+        ]),
+        "^ap",
+        regexp_is_match_scalar::<StringViewArray>,
+        [true, false, false, false]
+    );
+    test_flag_utf8_scalar!(
+        test_array_regexp_is_match_scalar_empty,
+        StringViewArray::from(vec![
+            "apache/arrow-rs",
+            "APACHE/ARROW-RS",
+            "parquet",
+            "PARQUET",
+        ]),
+        "",
+        regexp_is_match_scalar::<StringViewArray>,
+        [true, true, true, true]
+    );
+    test_flag_utf8_scalar!(
+        test_array_regexp_is_match_scalar_insensitive,
+        StringViewArray::from(vec![
+            "apache/arrow-rs",
+            "APACHE/ARROW-RS",
+            "parquet",
+            "PARQUET",
+        ]),
+        "^ap",
+        "i",
+        regexp_is_match_scalar::<StringViewArray>,
         [true, true, false, false]
     );
 }
diff --git a/arrow/benches/comparison_kernels.rs b/arrow/benches/comparison_kernels.rs
index c8aa7dfcf..4c4a63a77 100644
--- a/arrow/benches/comparison_kernels.rs
+++ b/arrow/benches/comparison_kernels.rs
@@ -15,19 +15,18 @@
 // specific language governing permissions and limitations
 // under the License.
 
+extern crate arrow;
 #[macro_use]
 extern crate criterion;
-use arrow::util::test_util::seedable_rng;
-use criterion::Criterion;
-
-extern crate arrow;
 
 use arrow::compute::kernels::cmp::*;
 use arrow::util::bench_util::*;
+use arrow::util::test_util::seedable_rng;
 use arrow::{array::*, datatypes::Float32Type, datatypes::Int32Type};
 use arrow_buffer::IntervalMonthDayNano;
 use arrow_string::like::*;
-use arrow_string::regexp::regexp_is_match_utf8_scalar;
+use arrow_string::regexp::regexp_is_match_scalar;
+use criterion::Criterion;
 use rand::rngs::StdRng;
 use rand::Rng;
 
@@ -53,8 +52,17 @@ fn bench_nilike_utf8_scalar(arr_a: &StringArray, value_b: &str) {
     nilike(arr_a, &StringArray::new_scalar(value_b)).unwrap();
 }
 
-fn bench_regexp_is_match_utf8_scalar(arr_a: &StringArray, value_b: &str) {
-    regexp_is_match_utf8_scalar(
+fn bench_stringview_regexp_is_match_scalar(arr_a: &StringViewArray, value_b: &str) {
+    regexp_is_match_scalar(
+        criterion::black_box(arr_a),
+        criterion::black_box(value_b),
+        None,
+    )
+    .unwrap();
+}
+
+fn bench_string_regexp_is_match_scalar(arr_a: &StringArray, value_b: &str) {
+    regexp_is_match_scalar(
         criterion::black_box(arr_a),
         criterion::black_box(value_b),
         None,
@@ -78,6 +86,7 @@ fn add_benchmark(c: &mut Criterion) {
     let arr_month_day_nano_b = create_month_day_nano_array_with_seed(SIZE, 0.0, 43);
 
     let arr_string = create_string_array::<i32>(SIZE, 0.0);
+    let arr_string_view = create_string_view_array(SIZE, 0.0);
 
     let scalar = Float32Array::from(vec![1.0]);
 
@@ -343,13 +352,45 @@ fn add_benchmark(c: &mut Criterion) {
         b.iter(|| bench_nilike_utf8_scalar(&arr_string, "%xx_xX%xXX"))
     });
 
-    c.bench_function("regexp_matches_utf8 scalar starts with", |b| {
-        b.iter(|| bench_regexp_is_match_utf8_scalar(&arr_string, "^xx"))
-    });
+    // StringArray: regexp_matches_utf8 scalar benchmarks
+    let mut group =
+        c.benchmark_group("StringArray: regexp_matches_utf8 scalar benchmarks".to_string());
 
-    c.bench_function("regexp_matches_utf8 scalar ends with", |b| {
-        b.iter(|| bench_regexp_is_match_utf8_scalar(&arr_string, "xx$"))
-    });
+    group
+        .bench_function("regexp_matches_utf8 scalar starts with", |b| {
+            b.iter(|| bench_string_regexp_is_match_scalar(&arr_string, "^xx"))
+        })
+        .bench_function("regexp_matches_utf8 scalar contains", |b| {
+            b.iter(|| bench_string_regexp_is_match_scalar(&arr_string, ".*xxXX.*"))
+        })
+        .bench_function("regexp_matches_utf8 scalar ends with", |b| {
+            b.iter(|| bench_string_regexp_is_match_scalar(&arr_string, "xx$"))
+        })
+        .bench_function("regexp_matches_utf8 scalar complex", |b| {
+            b.iter(|| bench_string_regexp_is_match_scalar(&arr_string, ".*x{2}.xX.*xXX"))
+        });
+
+    group.finish();
+
+    // StringViewArray: regexp_matches_utf8view scalar benchmarks
+    group =
+        c.benchmark_group("StringViewArray: regexp_matches_utf8view scalar benchmarks".to_string());
+
+    group
+        .bench_function("regexp_matches_utf8view scalar starts with", |b| {
+            b.iter(|| bench_stringview_regexp_is_match_scalar(&arr_string_view, "^xx"))
+        })
+        .bench_function("regexp_matches_utf8view scalar contains", |b| {
+            b.iter(|| bench_stringview_regexp_is_match_scalar(&arr_string_view, ".*xxXX.*"))
+        })
+        .bench_function("regexp_matches_utf8view scalar ends with", |b| {
+            b.iter(|| bench_stringview_regexp_is_match_scalar(&arr_string_view, "xx$"))
+        })
+        .bench_function("regexp_matches_utf8view scalar complex", |b| {
+            b.iter(|| bench_stringview_regexp_is_match_scalar(&arr_string_view, ".*x{2}.xX.*xXX"))
+        });
+
+    group.finish();
 
     // DictionaryArray benchmarks
 
diff --git a/arrow/src/compute/kernels.rs b/arrow/src/compute/kernels.rs
index 4eeb5892c..426952ebb 100644
--- a/arrow/src/compute/kernels.rs
+++ b/arrow/src/compute/kernels.rs
@@ -28,5 +28,8 @@ pub use arrow_string::{concat_elements, length, regexp, substring};
 pub mod comparison {
     pub use arrow_ord::comparison::*;
     pub use arrow_string::like::*;
+    // continue to export deprecated methods until they are removed
+    pub use arrow_string::regexp::{regexp_is_match, regexp_is_match_scalar};
+    #[allow(deprecated)]
     pub use arrow_string::regexp::{regexp_is_match_utf8, regexp_is_match_utf8_scalar};
 }
diff --git a/object_store/src/azure/builder.rs b/object_store/src/azure/builder.rs
index 0208073e8..35cedeafc 100644
--- a/object_store/src/azure/builder.rs
+++ b/object_store/src/azure/builder.rs
@@ -17,8 +17,8 @@
 
 use crate::azure::client::{AzureClient, AzureConfig};
 use crate::azure::credential::{
-    AzureAccessKey, AzureCliCredential, ClientSecretOAuthProvider, ImdsManagedIdentityProvider,
-    WorkloadIdentityOAuthProvider,
+    AzureAccessKey, AzureCliCredential, ClientSecretOAuthProvider, FabricTokenOAuthProvider,
+    ImdsManagedIdentityProvider, WorkloadIdentityOAuthProvider,
 };
 use crate::azure::{AzureCredential, AzureCredentialProvider, MicrosoftAzure, STORE};
 use crate::client::TokenCredentialProvider;
@@ -172,6 +172,14 @@ pub struct MicrosoftAzureBuilder {
     use_fabric_endpoint: ConfigValue<bool>,
     /// When set to true, skips tagging objects
     disable_tagging: ConfigValue<bool>,
+    /// Fabric token service url
+    fabric_token_service_url: Option<String>,
+    /// Fabric workload host
+    fabric_workload_host: Option<String>,
+    /// Fabric session token
+    fabric_session_token: Option<String>,
+    /// Fabric cluster identifier
+    fabric_cluster_identifier: Option<String>,
 }
 
 /// Configuration keys for [`MicrosoftAzureBuilder`]
@@ -336,6 +344,34 @@ pub enum AzureConfigKey {
     /// - `disable_tagging`
     DisableTagging,
 
+    /// Fabric token service url
+    ///
+    /// Supported keys:
+    /// - `azure_fabric_token_service_url`
+    /// - `fabric_token_service_url`
+    FabricTokenServiceUrl,
+
+    /// Fabric workload host
+    ///
+    /// Supported keys:
+    /// - `azure_fabric_workload_host`
+    /// - `fabric_workload_host`
+    FabricWorkloadHost,
+
+    /// Fabric session token
+    ///
+    /// Supported keys:
+    /// - `azure_fabric_session_token`
+    /// - `fabric_session_token`
+    FabricSessionToken,
+
+    /// Fabric cluster identifier
+    ///
+    /// Supported keys:
+    /// - `azure_fabric_cluster_identifier`
+    /// - `fabric_cluster_identifier`
+    FabricClusterIdentifier,
+
     /// Client options
     Client(ClientConfigKey),
 }
@@ -361,6 +397,10 @@ impl AsRef<str> for AzureConfigKey {
             Self::SkipSignature => "azure_skip_signature",
             Self::ContainerName => "azure_container_name",
             Self::DisableTagging => "azure_disable_tagging",
+            Self::FabricTokenServiceUrl => "azure_fabric_token_service_url",
+            Self::FabricWorkloadHost => "azure_fabric_workload_host",
+            Self::FabricSessionToken => "azure_fabric_session_token",
+            Self::FabricClusterIdentifier => "azure_fabric_cluster_identifier",
             Self::Client(key) => key.as_ref(),
         }
     }
@@ -406,6 +446,14 @@ impl FromStr for AzureConfigKey {
             "azure_skip_signature" | "skip_signature" => Ok(Self::SkipSignature),
             "azure_container_name" | "container_name" => Ok(Self::ContainerName),
             "azure_disable_tagging" | "disable_tagging" => Ok(Self::DisableTagging),
+            "azure_fabric_token_service_url" | "fabric_token_service_url" => {
+                Ok(Self::FabricTokenServiceUrl)
+            }
+            "azure_fabric_workload_host" | "fabric_workload_host" => Ok(Self::FabricWorkloadHost),
+            "azure_fabric_session_token" | "fabric_session_token" => Ok(Self::FabricSessionToken),
+            "azure_fabric_cluster_identifier" | "fabric_cluster_identifier" => {
+                Ok(Self::FabricClusterIdentifier)
+            }
             // Backwards compatibility
             "azure_allow_http" => Ok(Self::Client(ClientConfigKey::AllowHttp)),
             _ => match s.strip_prefix("azure_").unwrap_or(s).parse() {
@@ -525,6 +573,14 @@ impl MicrosoftAzureBuilder {
             }
             AzureConfigKey::ContainerName => self.container_name = Some(value.into()),
             AzureConfigKey::DisableTagging => self.disable_tagging.parse(value),
+            AzureConfigKey::FabricTokenServiceUrl => {
+                self.fabric_token_service_url = Some(value.into())
+            }
+            AzureConfigKey::FabricWorkloadHost => self.fabric_workload_host = Some(value.into()),
+            AzureConfigKey::FabricSessionToken => self.fabric_session_token = Some(value.into()),
+            AzureConfigKey::FabricClusterIdentifier => {
+                self.fabric_cluster_identifier = Some(value.into())
+            }
         };
         self
     }
@@ -561,6 +617,10 @@ impl MicrosoftAzureBuilder {
             AzureConfigKey::Client(key) => self.client_options.get_config_value(key),
             AzureConfigKey::ContainerName => self.container_name.clone(),
             AzureConfigKey::DisableTagging => Some(self.disable_tagging.to_string()),
+            AzureConfigKey::FabricTokenServiceUrl => self.fabric_token_service_url.clone(),
+            AzureConfigKey::FabricWorkloadHost => self.fabric_workload_host.clone(),
+            AzureConfigKey::FabricSessionToken => self.fabric_session_token.clone(),
+            AzureConfigKey::FabricClusterIdentifier => self.fabric_cluster_identifier.clone(),
         }
     }
 
@@ -856,6 +916,30 @@ impl MicrosoftAzureBuilder {
 
             let credential = if let Some(credential) = self.credentials {
                 credential
+            } else if let (
+                Some(fabric_token_service_url),
+                Some(fabric_workload_host),
+                Some(fabric_session_token),
+                Some(fabric_cluster_identifier),
+            ) = (
+                &self.fabric_token_service_url,
+                &self.fabric_workload_host,
+                &self.fabric_session_token,
+                &self.fabric_cluster_identifier,
+            ) {
+                // This case should precede the bearer token case because it is more specific and will utilize the bearer token.
+                let fabric_credential = FabricTokenOAuthProvider::new(
+                    fabric_token_service_url,
+                    fabric_workload_host,
+                    fabric_session_token,
+                    fabric_cluster_identifier,
+                    self.bearer_token.clone(),
+                );
+                Arc::new(TokenCredentialProvider::new(
+                    fabric_credential,
+                    self.client_options.client()?,
+                    self.retry_config.clone(),
+                )) as _
             } else if let Some(bearer_token) = self.bearer_token {
                 static_creds(AzureCredential::BearerToken(bearer_token))
             } else if let Some(access_key) = self.access_key {
diff --git a/object_store/src/azure/credential.rs b/object_store/src/azure/credential.rs
index 7808c7c4a..6b5fa19d1 100644
--- a/object_store/src/azure/credential.rs
+++ b/object_store/src/azure/credential.rs
@@ -22,7 +22,7 @@ use crate::client::{CredentialProvider, TokenProvider};
 use crate::util::hmac_sha256;
 use crate::RetryConfig;
 use async_trait::async_trait;
-use base64::prelude::BASE64_STANDARD;
+use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE_NO_PAD};
 use base64::Engine;
 use chrono::{DateTime, SecondsFormat, Utc};
 use reqwest::header::{
@@ -51,10 +51,15 @@ pub(crate) static BLOB_TYPE: HeaderName = HeaderName::from_static("x-ms-blob-typ
 pub(crate) static DELETE_SNAPSHOTS: HeaderName = HeaderName::from_static("x-ms-delete-snapshots");
 pub(crate) static COPY_SOURCE: HeaderName = HeaderName::from_static("x-ms-copy-source");
 static CONTENT_MD5: HeaderName = HeaderName::from_static("content-md5");
+static PARTNER_TOKEN: HeaderName = HeaderName::from_static("x-ms-partner-token");
+static CLUSTER_IDENTIFIER: HeaderName = HeaderName::from_static("x-ms-cluster-identifier");
+static WORKLOAD_RESOURCE: HeaderName = HeaderName::from_static("x-ms-workload-resource-moniker");
+static PROXY_HOST: HeaderName = HeaderName::from_static("x-ms-proxy-host");
 pub(crate) const RFC1123_FMT: &str = "%a, %d %h %Y %T GMT";
 const CONTENT_TYPE_JSON: &str = "application/json";
 const MSI_SECRET_ENV_KEY: &str = "IDENTITY_HEADER";
 const MSI_API_VERSION: &str = "2019-08-01";
+const TOKEN_MIN_TTL: u64 = 300;
 
 /// OIDC scope used when interacting with OAuth2 APIs
 ///
@@ -934,6 +939,113 @@ impl AzureCliCredential {
     }
 }
 
+/// Encapsulates the logic to perform an OAuth token challenge for Fabric
+#[derive(Debug)]
+pub struct FabricTokenOAuthProvider {
+    fabric_token_service_url: String,
+    fabric_workload_host: String,
+    fabric_session_token: String,
+    fabric_cluster_identifier: String,
+    storage_access_token: Option<String>,
+    token_expiry: Option<u64>,
+}
+
+#[derive(Debug, Deserialize)]
+struct Claims {
+    exp: u64,
+}
+
+impl FabricTokenOAuthProvider {
+    /// Create a new [`FabricTokenOAuthProvider`] for an azure backed store
+    pub fn new(
+        fabric_token_service_url: impl Into<String>,
+        fabric_workload_host: impl Into<String>,
+        fabric_session_token: impl Into<String>,
+        fabric_cluster_identifier: impl Into<String>,
+        storage_access_token: Option<String>,
+    ) -> Self {
+        let (storage_access_token, token_expiry) = match storage_access_token {
+            Some(token) => match Self::validate_and_get_expiry(&token) {
+                Some(expiry) if expiry > Self::get_current_timestamp() + TOKEN_MIN_TTL => {
+                    (Some(token), Some(expiry))
+                }
+                _ => (None, None),
+            },
+            None => (None, None),
+        };
+
+        Self {
+            fabric_token_service_url: fabric_token_service_url.into(),
+            fabric_workload_host: fabric_workload_host.into(),
+            fabric_session_token: fabric_session_token.into(),
+            fabric_cluster_identifier: fabric_cluster_identifier.into(),
+            storage_access_token,
+            token_expiry,
+        }
+    }
+
+    fn validate_and_get_expiry(token: &str) -> Option<u64> {
+        let payload = token.split('.').nth(1)?;
+        let decoded_bytes = BASE64_URL_SAFE_NO_PAD.decode(payload).ok()?;
+        let decoded_str = str::from_utf8(&decoded_bytes).ok()?;
+        let claims: Claims = serde_json::from_str(decoded_str).ok()?;
+        Some(claims.exp)
+    }
+
+    fn get_current_timestamp() -> u64 {
+        SystemTime::now()
+            .duration_since(SystemTime::UNIX_EPOCH)
+            .map_or(0, |d| d.as_secs())
+    }
+}
+
+#[async_trait::async_trait]
+impl TokenProvider for FabricTokenOAuthProvider {
+    type Credential = AzureCredential;
+
+    /// Fetch a token
+    async fn fetch_token(
+        &self,
+        client: &Client,
+        retry: &RetryConfig,
+    ) -> crate::Result<TemporaryToken<Arc<AzureCredential>>> {
+        if let Some(storage_access_token) = &self.storage_access_token {
+            if let Some(expiry) = self.token_expiry {
+                let exp_in = expiry - Self::get_current_timestamp();
+                if exp_in > TOKEN_MIN_TTL {
+                    return Ok(TemporaryToken {
+                        token: Arc::new(AzureCredential::BearerToken(storage_access_token.clone())),
+                        expiry: Some(Instant::now() + Duration::from_secs(exp_in)),
+                    });
+                }
+            }
+        }
+
+        let query_items = vec![("resource", AZURE_STORAGE_RESOURCE)];
+        let access_token: String = client
+            .request(Method::GET, &self.fabric_token_service_url)
+            .header(&PARTNER_TOKEN, self.fabric_session_token.as_str())
+            .header(&CLUSTER_IDENTIFIER, self.fabric_cluster_identifier.as_str())
+            .header(&WORKLOAD_RESOURCE, self.fabric_cluster_identifier.as_str())
+            .header(&PROXY_HOST, self.fabric_workload_host.as_str())
+            .query(&query_items)
+            .retryable(retry)
+            .idempotent(true)
+            .send()
+            .await
+            .context(TokenRequestSnafu)?
+            .text()
+            .await
+            .context(TokenResponseBodySnafu)?;
+        let exp_in = Self::validate_and_get_expiry(&access_token)
+            .map_or(3600, |expiry| expiry - Self::get_current_timestamp());
+        Ok(TemporaryToken {
+            token: Arc::new(AzureCredential::BearerToken(access_token)),
+            expiry: Some(Instant::now() + Duration::from_secs(exp_in)),
+        })
+    }
+}
+
 #[async_trait]
 impl CredentialProvider for AzureCliCredential {
     type Credential = AzureCredential;
diff --git a/object_store/src/client/mod.rs b/object_store/src/client/mod.rs
index 9a3b70554..b9688bc5d 100644
--- a/object_store/src/client/mod.rs
+++ b/object_store/src/client/mod.rs
@@ -94,6 +94,8 @@ pub enum ClientConfigKey {
     Http2KeepAliveTimeout,
     /// Enable HTTP2 keep alive pings for idle connections
     Http2KeepAliveWhileIdle,
+    /// Sets the maximum frame size to use for HTTP2.
+    Http2MaxFrameSize,
     /// Only use http2 connections
     Http2Only,
     /// The pool max idle timeout
@@ -129,6 +131,7 @@ impl AsRef<str> for ClientConfigKey {
             Self::Http2KeepAliveInterval => "http2_keep_alive_interval",
             Self::Http2KeepAliveTimeout => "http2_keep_alive_timeout",
             Self::Http2KeepAliveWhileIdle => "http2_keep_alive_while_idle",
+            Self::Http2MaxFrameSize => "http2_max_frame_size",
             Self::PoolIdleTimeout => "pool_idle_timeout",
             Self::PoolMaxIdlePerHost => "pool_max_idle_per_host",
             Self::ProxyUrl => "proxy_url",
@@ -154,6 +157,7 @@ impl FromStr for ClientConfigKey {
             "http2_keep_alive_interval" => Ok(Self::Http2KeepAliveInterval),
             "http2_keep_alive_timeout" => Ok(Self::Http2KeepAliveTimeout),
             "http2_keep_alive_while_idle" => Ok(Self::Http2KeepAliveWhileIdle),
+            "http2_max_frame_size" => Ok(Self::Http2MaxFrameSize),
             "pool_idle_timeout" => Ok(Self::PoolIdleTimeout),
             "pool_max_idle_per_host" => Ok(Self::PoolMaxIdlePerHost),
             "proxy_url" => Ok(Self::ProxyUrl),
@@ -238,6 +242,7 @@ pub struct ClientOptions {
     http2_keep_alive_interval: Option<ConfigValue<Duration>>,
     http2_keep_alive_timeout: Option<ConfigValue<Duration>>,
     http2_keep_alive_while_idle: ConfigValue<bool>,
+    http2_max_frame_size: Option<ConfigValue<u32>>,
     http1_only: ConfigValue<bool>,
     http2_only: ConfigValue<bool>,
 }
@@ -269,6 +274,7 @@ impl Default for ClientOptions {
             http2_keep_alive_interval: None,
             http2_keep_alive_timeout: None,
             http2_keep_alive_while_idle: Default::default(),
+            http2_max_frame_size: None,
             // HTTP2 is known to be significantly slower than HTTP1, so we default
             // to HTTP1 for now.
             // https://github.com/apache/arrow-rs/issues/5194
@@ -304,6 +310,9 @@ impl ClientOptions {
             ClientConfigKey::Http2KeepAliveWhileIdle => {
                 self.http2_keep_alive_while_idle.parse(value)
             }
+            ClientConfigKey::Http2MaxFrameSize => {
+                self.http2_max_frame_size = Some(ConfigValue::Deferred(value.into()))
+            }
             ClientConfigKey::PoolIdleTimeout => {
                 self.pool_idle_timeout = Some(ConfigValue::Deferred(value.into()))
             }
@@ -338,6 +347,9 @@ impl ClientOptions {
             ClientConfigKey::Http2KeepAliveWhileIdle => {
                 Some(self.http2_keep_alive_while_idle.to_string())
             }
+            ClientConfigKey::Http2MaxFrameSize => {
+                self.http2_max_frame_size.as_ref().map(|v| v.to_string())
+            }
             ClientConfigKey::Http2Only => Some(self.http2_only.to_string()),
             ClientConfigKey::PoolIdleTimeout => self.pool_idle_timeout.as_ref().map(fmt_duration),
             ClientConfigKey::PoolMaxIdlePerHost => {
@@ -541,6 +553,14 @@ impl ClientOptions {
         self
     }
 
+    /// Sets the maximum frame size to use for HTTP2.
+    ///
+    /// Default is currently 16,384 but may change internally to optimize for common uses.
+    pub fn with_http2_max_frame_size(mut self, sz: u32) -> Self {
+        self.http2_max_frame_size = Some(ConfigValue::Parsed(sz));
+        self
+    }
+
     /// Get the mime type for the file in `path` to be uploaded
     ///
     /// Gets the file extension from `path`, and returns the
@@ -635,6 +655,10 @@ impl ClientOptions {
             builder = builder.http2_keep_alive_while_idle(true)
         }
 
+        if let Some(sz) = &self.http2_max_frame_size {
+            builder = builder.http2_max_frame_size(Some(sz.get()?))
+        }
+
         if self.http1_only.get()? {
             builder = builder.http1_only()
         }
@@ -799,6 +823,7 @@ mod tests {
         let http2_keep_alive_interval = "90 seconds".to_string();
         let http2_keep_alive_timeout = "91 seconds".to_string();
         let http2_keep_alive_while_idle = "92 seconds".to_string();
+        let http2_max_frame_size = "1337".to_string();
         let pool_idle_timeout = "93 seconds".to_string();
         let pool_max_idle_per_host = "94".to_string();
         let proxy_url = "https://fake_proxy_url".to_string();
@@ -824,6 +849,7 @@ mod tests {
                 "http2_keep_alive_while_idle",
                 http2_keep_alive_while_idle.clone(),
             ),
+            ("http2_max_frame_size", http2_max_frame_size.clone()),
             ("pool_idle_timeout", pool_idle_timeout.clone()),
             ("pool_max_idle_per_host", pool_max_idle_per_host.clone()),
             ("proxy_url", proxy_url.clone()),
@@ -891,6 +917,12 @@ mod tests {
                 .unwrap(),
             http2_keep_alive_while_idle
         );
+        assert_eq!(
+            builder
+                .get_config_value(&ClientConfigKey::Http2MaxFrameSize)
+                .unwrap(),
+            http2_max_frame_size
+        );
 
         assert_eq!(
             builder
diff --git a/object_store/src/config.rs b/object_store/src/config.rs
index 987e6e420..f715fa082 100644
--- a/object_store/src/config.rs
+++ b/object_store/src/config.rs
@@ -103,6 +103,15 @@ impl Parse for usize {
     }
 }
 
+impl Parse for u32 {
+    fn parse(v: &str) -> Result<Self> {
+        Self::from_str(v).map_err(|_| Error::Generic {
+            store: "Config",
+            source: format!("failed to parse \"{v}\" as u32").into(),
+        })
+    }
+}
+
 impl Parse for HeaderValue {
     fn parse(v: &str) -> Result<Self> {
         Self::from_str(v).map_err(|_| Error::Generic {
diff --git a/parquet/src/arrow/array_reader/mod.rs b/parquet/src/arrow/array_reader/mod.rs
index fef1e8722..a5ea426e9 100644
--- a/parquet/src/arrow/array_reader/mod.rs
+++ b/parquet/src/arrow/array_reader/mod.rs
@@ -61,12 +61,16 @@ pub use struct_array::StructArrayReader;
 
 /// Array reader reads parquet data into arrow array.
 pub trait ArrayReader: Send {
+    // TODO: this function is never used, and the trait is not public. Perhaps this should be
+    // removed.
+    #[allow(dead_code)]
     fn as_any(&self) -> &dyn Any;
 
     /// Returns the arrow type of this array reader.
     fn get_data_type(&self) -> &ArrowType;
 
     /// Reads at most `batch_size` records into an arrow array and return it.
+    #[cfg(any(feature = "experimental", test))]
     fn next_batch(&mut self, batch_size: usize) -> Result<ArrayRef> {
         self.read_records(batch_size)?;
         self.consume_batch()
diff --git a/parquet_derive/src/lib.rs b/parquet_derive/src/lib.rs
index 038d8fa44..a35cebb3a 100644
--- a/parquet_derive/src/lib.rs
+++ b/parquet_derive/src/lib.rs
@@ -15,6 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
+//! This crate provides a procedural macro to derive
+//! implementations of a RecordWriter and RecordReader
+
+#![warn(missing_docs)]
 #![recursion_limit = "128"]
 
 extern crate proc_macro;


Test patch:
diff --git a/arrow-pyarrow-integration-testing/src/lib.rs b/arrow-pyarrow-integration-testing/src/lib.rs
index 918fa74e3..e12c1389e 100644
--- a/arrow-pyarrow-integration-testing/src/lib.rs
+++ b/arrow-pyarrow-integration-testing/src/lib.rs
@@ -18,6 +18,7 @@
 //! This library demonstrates a minimal usage of Rust's C data interface to pass
 //! arrays from and to Python.
 
+#![warn(missing_docs)]
 use std::sync::Arc;
 
 use arrow::array::new_empty_array;
diff --git a/arrow/tests/csv.rs b/arrow/tests/csv.rs
index fd01f1663..781b750f3 100644
--- a/arrow/tests/csv.rs
+++ b/arrow/tests/csv.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use core::str;
 use std::sync::Arc;
 
 use arrow_array::*;
@@ -54,6 +55,6 @@ fn test_export_csv_timestamps() {
     let left = "c1,c2
 2019-04-18T20:54:47.378+10:00,2019-04-18T10:54:47.378
 2021-10-30T17:59:07+11:00,2021-10-30T06:59:07\n";
-    let right = String::from_utf8(sw).unwrap();
+    let right = str::from_utf8(&sw).unwrap();
     assert_eq!(left, right);
 }