Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
ae27070
[Variant] Align cast logic for from/to_decimal for variant (#9689)
klion26 May 18, 2026
da09872
Implement native interleave for ListView (#9558)
vegarsti May 20, 2026
d48c305
fix(arrow-schema): allow empty metadata value for UUID extension type…
asubiotto May 20, 2026
accb1cf
[parquet] Allow more encryption algorithms (#9203)
hsiang-c May 20, 2026
7c6eb2c
feat(parquet): Add `ParquetPushDecoder::into_builder` to allow swappi…
adriangb May 20, 2026
e7c37de
Add helper functions to create `LogicalType` struct variants (#9996)
etseidl May 20, 2026
39c8814
feat: extract `has_false` and `has_true` from BooleanArray to `Boolea…
rluvaton May 20, 2026
73c513a
Bulk-fill definition levels for majority-null leaf columns (#9967)
RyanJamesStewart May 20, 2026
dc821a9
Add support for FixedSizeList to variant_to_arrow (#9663)
rishvin May 20, 2026
4b80f0e
feat(parquet): generalize value encoder inputs (#9955)
HippoBaro May 20, 2026
f790721
fix(parquet): validate INT96 column metadata statistics (#10003)
fallintoplace May 21, 2026
c46f419
fix(cast): Trying to fix cast losting schema problem (#10005)
mapleFU May 21, 2026
2f923f7
fix(arrow-cast): support full Date32 range when parsing extended-year…
swanandx May 22, 2026
edfb9ab
Use Thrift macro to generate Parquet `LogicalType` serialization code…
etseidl May 22, 2026
8c88bd4
Adds is_null function to RowAccessor (#9979)
choubacha May 22, 2026
5372e8a
Fix parquet-variant build on wasm targets (#9978)
AdamGS May 22, 2026
df89537
[Variant] remove `BorrowedShreddingState` (#9791)
sdf-jkl May 22, 2026
7b335b7
fix: prevent panic in record reader when row group metadata overcount…
BoazC-MSFT May 22, 2026
8acab7b
feat: Implement decimal <-> float16 casts (#10008)
neilconway May 25, 2026
1c679ef
fix(parquet): exclude single-leaf struct roots from predicate cache (…
imhy May 25, 2026
aa61e07
[arrow-select] Replace `ArrayData` with direct `Array` construction i…
liamzwbao May 25, 2026
c4e154f
Support Shredded Lists/Array in `variant_get` (#8354)
sdf-jkl May 25, 2026
e28fd0d
Add `DatePart` enum 1-indexed variants (#9965)
sdf-jkl May 25, 2026
2b2a95a
arrow: add oversized coalesce take benchmarks (#9799)
ClSlaid May 26, 2026
bbbe8a6
bench(parquet): add short and large string `arrow_writer` benchmarks …
adriangb May 26, 2026
40ebcf0
benchmarks for writing REE arrays to parquet (#9936)
Rich-T-kid May 27, 2026
2eeb805
Implement AnyRee (#9959)
Rich-T-kid May 27, 2026
e470187
fix: Reject empty strings when casting strings to decimal (#10010)
neilconway May 27, 2026
1377761
Validate FIXED_LEN_BYTE_ARRAY length for DECIMAL and INTERVAL types (…
CynicDog May 28, 2026
9450f1e
Call `align_buffers()` in `from_ffi`, remove redundant call from `arr…
mbutrovich May 29, 2026
511ad06
bench(parquet): add Sbbf check/insert benchmarks (#10041)
dmatth1 May 31, 2026
c58f0b1
chore: Remove some deprecated functions from parquet crate (#10035)
etseidl Jun 1, 2026
38778f0
Replace `From<Vec<_>>` impls with `TryFrom`s for `FixedSizeBinaryArra…
quantumish Jun 1, 2026
51ffd8c
fix: better error handling for negative size of FixedSizeBinary (#10042)
theirix Jun 2, 2026
6881f73
Adjust Variant size expectation for s390x architecture (#10027)
frantisekz Jun 2, 2026
57eeb26
arrow-cast: Add ability to cast plain struct to dictionary (#10039)
brancz Jun 2, 2026
870fb06
test: add overflow tests for MutableBuffer (#9958)
SoimanVasile Jun 2, 2026
1ae2469
chore: Remove some deprecated Arrow functions from the public API (#1…
etseidl Jun 2, 2026
cfc2b88
Add coalesce inline-view filter benchmarks (#10050)
ClSlaid Jun 2, 2026
259cff2
feat(parquet): add uses_key_retriever method to FileDecryptionPropert…
adamreeve Jun 2, 2026
f03e1bc
fix(ipc): handle duplicate projection indices in IPC reader (#9952)
pchintar Jun 3, 2026
f001b22
[#10029][benchmarks] arrow-flight roundtrip as well as encode/decode …
Rich-T-kid Jun 3, 2026
4e7a2fa
Improve `take_bytes` perf in the null cases between 10-25% (#9625)
AdamGS Jun 3, 2026
58bdc7d
arrow-buffer: i256: Implement num_traits wrapping shift (#9418)
theirix Jun 3, 2026
9949226
perf(parquet): LevelInfoBuilder batch write when no repetition childs…
mapleFU Jun 3, 2026
6c39797
Improve email created by create_tarball.sh script (#9944)
alamb Jun 3, 2026
97f4b14
arrow-buffer: i256: implement ilog (#9453)
theirix Jun 3, 2026
2a1d40d
Reduce Miri runtime even more (#9650)
AdamGS Jun 3, 2026
fa4b19a
fix(parquet): bound page byte size for large variable-width values
adriangb Jun 3, 2026
0eee878
fix(parquet): bound page size for arrow byte-array column writes
adriangb Jun 3, 2026
c654237
test(parquet): page-layout coverage for byte-budget page sizing
adriangb Jun 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 86 additions & 18 deletions arrow-arith/src/temporal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use arrow_array::cast::AsArray;
use cast::as_primitive_array;
use chrono::{Datelike, TimeZone, Timelike, Utc};

use arrow_array::ree_map;
use arrow_array::temporal_conversions::{
MICROSECONDS, MICROSECONDS_IN_DAY, MILLISECONDS, MILLISECONDS_IN_DAY, NANOSECONDS,
NANOSECONDS_IN_DAY, SECONDS_IN_DAY, date32_to_datetime, date64_to_datetime,
Expand Down Expand Up @@ -63,6 +62,10 @@ pub enum DatePart {
DayOfWeekSunday0,
/// Day of the week, in range `0..=6`, where Monday is `0`
DayOfWeekMonday0,
/// Day of the week, in range `1..=7`, where Sunday is `1`
DayOfWeekSunday1,
/// ISO day of the week, in range `1..=7`, where Monday is `1`
DayOfWeekMonday1,
/// Day of year, in range `1..=366`
DayOfYear,
/// Hour of the day, in range `0..=23`
Expand Down Expand Up @@ -100,11 +103,6 @@ impl std::fmt::Display for DatePart {
/// - `century`, `decade`, `millennium` — no matching [`DatePart`] variant.
/// - `timezone`, `timezone_hour`, `timezone_minute` — not modeled by
/// [`DatePart`].
/// - `isodow` — PostgreSQL's `isodow` returns `1..=7` (Mon=1), but the
/// closest variant ([`DatePart::DayOfWeekMonday0`]) returns `0..=6`.
/// Accepting it here would silently shift the value range; callers that
/// need the PostgreSQL semantic should map the alias themselves and
/// add `1` to the extracted value.
impl FromStr for DatePart {
type Err = ArrowError;

Expand All @@ -118,6 +116,9 @@ impl FromStr for DatePart {
"isoweek" => Self::WeekISO,
"d" | "day" | "days" => Self::Day,
"dow" | "dayofweek" => Self::DayOfWeekSunday0,
"dow1" | "dayofweek1" => Self::DayOfWeekSunday1,
"isodow0" => Self::DayOfWeekMonday0,
"isodow" => Self::DayOfWeekMonday1,
"doy" | "dayofyear" => Self::DayOfYear,
"h" | "hr" | "hrs" | "hour" | "hours" => Self::Hour,
"m" | "min" | "mins" | "minute" | "minutes" => Self::Minute,
Expand Down Expand Up @@ -158,6 +159,8 @@ where
DatePart::Day => |d| d.day() as i32,
DatePart::DayOfWeekSunday0 => |d| d.num_days_from_sunday(),
DatePart::DayOfWeekMonday0 => |d| d.num_days_from_monday(),
DatePart::DayOfWeekSunday1 => |d| d.num_days_from_sunday() + 1,
DatePart::DayOfWeekMonday1 => |d| d.num_days_from_monday() + 1,
DatePart::DayOfYear => |d| d.ordinal() as i32,
DatePart::Hour => |d| d.hour() as i32,
DatePart::Minute => |d| d.minute() as i32,
Expand Down Expand Up @@ -249,15 +252,12 @@ pub fn date_part(array: &dyn Array, part: DatePart) -> Result<ArrayRef, ArrowErr
let new_array = array.with_values(values);
Ok(new_array)
}
DataType::RunEndEncoded(k, _) => match k.data_type() {
DataType::Int16 => ree_map!(array, Int16Type, |a| date_part(a, part)),
DataType::Int32 => ree_map!(array, Int32Type, |a| date_part(a, part)),
DataType::Int64 => ree_map!(array, Int64Type, |a| date_part(a, part)),
_ => Err(ArrowError::InvalidArgumentError(format!(
"Invalid run-end type: {:?}",
k.data_type()
))),
},
DataType::RunEndEncoded(_, _) => {
let array = array.as_any_ree();
let values = date_part(array.values(), part)?;
let new_array = array.with_values(values);
Ok(new_array)
}
t => return_compute_error_with!(format!("{part} does not support"), t),
)
}
Expand Down Expand Up @@ -500,6 +500,8 @@ impl ExtractDatePartExt for PrimitiveArray<IntervalYearMonthType> {
| DatePart::Day
| DatePart::DayOfWeekSunday0
| DatePart::DayOfWeekMonday0
| DatePart::DayOfWeekSunday1
| DatePart::DayOfWeekMonday1
| DatePart::DayOfYear
| DatePart::Hour
| DatePart::Minute
Expand Down Expand Up @@ -536,6 +538,8 @@ impl ExtractDatePartExt for PrimitiveArray<IntervalDayTimeType> {
| DatePart::Month
| DatePart::DayOfWeekSunday0
| DatePart::DayOfWeekMonday0
| DatePart::DayOfWeekSunday1
| DatePart::DayOfWeekMonday1
| DatePart::DayOfYear => {
return_compute_error_with!(format!("{part} does not support"), self.data_type())
}
Expand Down Expand Up @@ -578,6 +582,8 @@ impl ExtractDatePartExt for PrimitiveArray<IntervalMonthDayNanoType> {
| DatePart::YearISO
| DatePart::DayOfWeekSunday0
| DatePart::DayOfWeekMonday0
| DatePart::DayOfWeekSunday1
| DatePart::DayOfWeekMonday1
| DatePart::DayOfYear => {
return_compute_error_with!(format!("{part} does not support"), self.data_type())
}
Expand Down Expand Up @@ -610,6 +616,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationSecondType> {
| DatePart::Month
| DatePart::DayOfWeekSunday0
| DatePart::DayOfWeekMonday0
| DatePart::DayOfWeekSunday1
| DatePart::DayOfWeekMonday1
| DatePart::DayOfYear => {
return_compute_error_with!(format!("{part} does not support"), self.data_type())
}
Expand Down Expand Up @@ -642,6 +650,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationMillisecondType> {
| DatePart::Month
| DatePart::DayOfWeekSunday0
| DatePart::DayOfWeekMonday0
| DatePart::DayOfWeekSunday1
| DatePart::DayOfWeekMonday1
| DatePart::DayOfYear => {
return_compute_error_with!(format!("{part} does not support"), self.data_type())
}
Expand Down Expand Up @@ -674,6 +684,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationMicrosecondType> {
| DatePart::Month
| DatePart::DayOfWeekSunday0
| DatePart::DayOfWeekMonday0
| DatePart::DayOfWeekSunday1
| DatePart::DayOfWeekMonday1
| DatePart::DayOfYear => {
return_compute_error_with!(format!("{part} does not support"), self.data_type())
}
Expand Down Expand Up @@ -706,6 +718,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationNanosecondType> {
| DatePart::Month
| DatePart::DayOfWeekSunday0
| DatePart::DayOfWeekMonday0
| DatePart::DayOfWeekSunday1
| DatePart::DayOfWeekMonday1
| DatePart::DayOfYear => {
return_compute_error_with!(format!("{part} does not support"), self.data_type())
}
Expand Down Expand Up @@ -985,6 +999,46 @@ mod tests {
assert_eq!(2, b.value(2));
}

#[test]
fn test_temporal_array_date64_dayofweek1() {
//1514764800000 -> 2018-01-01 (Monday)
//1550636625000 -> 2019-02-20 (Wednesday)
//1483228800000 -> 2017-01-01 (Sunday)
let a: PrimitiveArray<Date64Type> = vec![
Some(1514764800000),
None,
Some(1550636625000),
Some(1483228800000),
]
.into();

let b = date_part_primitive(&a, DatePart::DayOfWeekSunday1).unwrap();
assert_eq!(2, b.value(0));
assert!(!b.is_valid(1));
assert_eq!(4, b.value(2));
assert_eq!(1, b.value(3));
}

#[test]
fn test_temporal_array_date64_isodow() {
//1514764800000 -> 2018-01-01 (Monday)
//1550636625000 -> 2019-02-20 (Wednesday)
//1483228800000 -> 2017-01-01 (Sunday)
let a: PrimitiveArray<Date64Type> = vec![
Some(1514764800000),
None,
Some(1550636625000),
Some(1483228800000),
]
.into();

let b = date_part_primitive(&a, DatePart::DayOfWeekMonday1).unwrap();
assert_eq!(1, b.value(0));
assert!(!b.is_valid(1));
assert_eq!(3, b.value(2));
assert_eq!(7, b.value(3));
}

#[test]
fn test_temporal_array_date64_weekday0() {
//1483228800000 -> 2017-01-01 (Sunday)
Expand Down Expand Up @@ -1579,11 +1633,15 @@ mod tests {
let invalid_parts = [
DatePart::Quarter,
DatePart::Year,
DatePart::YearISO,
DatePart::Month,
DatePart::Week,
DatePart::WeekISO,
DatePart::Day,
DatePart::DayOfWeekSunday0,
DatePart::DayOfWeekMonday0,
DatePart::DayOfWeekSunday1,
DatePart::DayOfWeekMonday1,
DatePart::DayOfYear,
];

Expand Down Expand Up @@ -1813,8 +1871,12 @@ mod tests {
fn ensure_returns_error(array: &dyn Array) {
let invalid_parts = [
DatePart::Quarter,
DatePart::YearISO,
DatePart::WeekISO,
DatePart::DayOfWeekSunday0,
DatePart::DayOfWeekMonday0,
DatePart::DayOfWeekSunday1,
DatePart::DayOfWeekMonday1,
DatePart::DayOfYear,
];

Expand Down Expand Up @@ -1956,10 +2018,14 @@ mod tests {
fn ensure_returns_error(array: &dyn Array) {
let invalid_parts = [
DatePart::Year,
DatePart::YearISO,
DatePart::Quarter,
DatePart::Month,
DatePart::WeekISO,
DatePart::DayOfWeekSunday0,
DatePart::DayOfWeekMonday0,
DatePart::DayOfWeekSunday1,
DatePart::DayOfWeekMonday1,
DatePart::DayOfYear,
];

Expand Down Expand Up @@ -2157,6 +2223,11 @@ mod tests {
("day", DatePart::Day),
("dow", DatePart::DayOfWeekSunday0),
("DayOfWeek", DatePart::DayOfWeekSunday0),
("dow1", DatePart::DayOfWeekSunday1),
("dayofweek1", DatePart::DayOfWeekSunday1),
("DAYOFWEEK1", DatePart::DayOfWeekSunday1),
("isodow", DatePart::DayOfWeekMonday1),
("ISODOW", DatePart::DayOfWeekMonday1),
("doy", DatePart::DayOfYear),
("DayOfYear", DatePart::DayOfYear),
("h", DatePart::Hour),
Expand Down Expand Up @@ -2195,8 +2266,6 @@ mod tests {
#[test]
fn test_date_part_from_str_unknown() {
// Names intentionally rejected — see the FromStr doc comment for why.
// `isodow` is here because mapping it to `DayOfWeekMonday0` would
// silently shift the value range vs. PostgreSQL's `isodow` (1..=7).
let unknown = [
"epoch",
"century",
Expand All @@ -2205,7 +2274,6 @@ mod tests {
"timezone",
"timezone_hour",
"timezone_minute",
"isodow",
// Whitespace is not trimmed — pin this so the behavior doesn't
// change silently. Callers should trim before parsing.
" year ",
Expand Down
52 changes: 2 additions & 50 deletions arrow-array/src/array/boolean_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use crate::array::print_long_array;
use crate::builder::BooleanBuilder;
use crate::iterator::BooleanIter;
use crate::{Array, ArrayAccessor, ArrayRef, Scalar};
use arrow_buffer::bit_chunk_iterator::UnalignedBitChunk;
use arrow_buffer::{BooleanBuffer, Buffer, MutableBuffer, NullBuffer, bit_util};
use arrow_data::{ArrayData, ArrayDataBuilder};
use arrow_schema::DataType;
Expand Down Expand Up @@ -157,16 +156,6 @@ impl BooleanArray {
&self.values
}

/// Block size for chunked fold operations in [`Self::has_true`] and [`Self::has_false`].
/// Using `chunks_exact` with this size lets the compiler fully unroll the inner
/// fold (no inner branch/loop), enabling short-circuit exits every N chunks.
const CHUNK_FOLD_BLOCK_SIZE: usize = 16;

/// Returns an [`UnalignedBitChunk`] over this array's values.
fn unaligned_bit_chunks(&self) -> UnalignedBitChunk<'_> {
UnalignedBitChunk::new(self.values().values(), self.values().offset(), self.len())
}

/// Returns the number of non null, true values within this array.
/// If you only need to check if there is at least one true value, consider using `has_true()` which can short-circuit and be more efficient.
pub fn true_count(&self) -> usize {
Expand Down Expand Up @@ -202,16 +191,7 @@ impl BooleanArray {
let value_chunks = self.values().bit_chunks().iter_padded();
null_chunks.zip(value_chunks).any(|(n, v)| (n & v) != 0)
}
None => {
let bit_chunks = self.unaligned_bit_chunks();
let chunks = bit_chunks.chunks();
let mut exact = chunks.chunks_exact(Self::CHUNK_FOLD_BLOCK_SIZE);
let found = bit_chunks.prefix().unwrap_or(0) != 0
|| exact.any(|block| block.iter().fold(0u64, |acc, &c| acc | c) != 0);
found
|| exact.remainder().iter().any(|&c| c != 0)
|| bit_chunks.suffix().unwrap_or(0) != 0
}
None => self.values().has_true(),
}
}

Expand All @@ -228,35 +208,7 @@ impl BooleanArray {
let value_chunks = self.values().bit_chunks().iter_padded();
null_chunks.zip(value_chunks).any(|(n, v)| (n & !v) != 0)
}
None => {
let bit_chunks = self.unaligned_bit_chunks();
// UnalignedBitChunk zeros padding bits; fill them with 1s so
// they don't appear as false values.
let lead_mask = !((1u64 << bit_chunks.lead_padding()) - 1);
let trail_mask = if bit_chunks.trailing_padding() == 0 {
u64::MAX
} else {
(1u64 << (64 - bit_chunks.trailing_padding())) - 1
};
let (prefix_fill, suffix_fill) = match (bit_chunks.prefix(), bit_chunks.suffix()) {
(Some(_), Some(_)) => (!lead_mask, !trail_mask),
(Some(_), None) => (!lead_mask | !trail_mask, 0),
(None, Some(_)) => (0, !trail_mask),
(None, None) => (0, 0),
};
let chunks = bit_chunks.chunks();
let mut exact = chunks.chunks_exact(Self::CHUNK_FOLD_BLOCK_SIZE);
let found = bit_chunks
.prefix()
.is_some_and(|v| (v | prefix_fill) != u64::MAX)
|| exact
.any(|block| block.iter().fold(u64::MAX, |acc, &c| acc & c) != u64::MAX);
found
|| exact.remainder().iter().any(|&c| c != u64::MAX)
|| bit_chunks
.suffix()
.is_some_and(|v| (v | suffix_fill) != u64::MAX)
}
None => self.values().has_false(),
}
}

Expand Down
Loading
Loading