You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
date_part returning wrong results due to overflows #14738
When playing with the date_part function, I see that there's ways of triggering int32 multiplication overflows that either panic on a debug build, or return the wrong number at runtime.
To Reproduce
Executing the following statement shows the behavior.
I gave this a look and was able to fix it by casting duration array to nanosecond and then extracting components. However, it seemed like a fragile workaround so I was not sure of opening a PR.
Smth like this:
fn duration_part(array: &dyn Array, unit: IntervalUnit) -> Result<ArrayRef> {
const NANOS_PER_MINUTE: i64 = 60 * 1_000_000_000;
let divisor = match unit {
Second => 1_000_000_000,
Millisecond => 1_000_000,
Microsecond => 1_000,
Nanosecond => 1,
};
let nanos_array = cast(array, &Duration(Nanosecond))?;
let result = nanos_array.unary(|d| (d % NANOS_PER_MINUTE) / divisor);
Ok(Arc::new(result))
}
This currently panics in debug builds at date_part.rs:426 with attempt to multiply with overflow; release builds silently wrap and return an incorrect value.
I also confirmed that the interval-semantics portion of this issue was fixed by #14817, so I intend to target only the remaining Duration path.
My current plan is to perform the subsecond calculation using i64 internally while preserving the existing Int32 return type. For values outside the Int32 range, should this return an error or saturate/clamp?
@alamb — you previously suggested the duration-part work might belong in arrow-rs. My proposed change is limited to fixing the existing i32 arithmetic in DataFusion's date_part.rs rather than introducing an upstream kernel. Would a local fix be appropriate here?
If this approach sounds good, I'd be happy to take this issue.
I also confirmed that the interval-semantics portion of this issue was fixed by #14817, so I intend to target only the remaining Duration path.
@Nisarg0007 are you still working on this? Interval also drops the fraction past i32::MAX nanoseconds, as Duration does:
SELECT extract(millisecond from interval '62.5 seconds'),
extract(millisecond from arrow_cast(2500000000, 'Duration(Nanosecond)'));
-- 2000, 2000 (expected 2500, 2500)
As I read it, this branch treats the overflowed nanoseconds as zero.
Would a local fix be appropriate here?
#26676 fixes the same overflow for EXTRACT(EPOCH) in date_part.rs by computing from the i64 value. Should second, millisecond and microsecond do the same?
In Postgres the difference is an interval, so microsecond is the seconds field within the minute (0 here). In DataFusion it is a Duration, and #26686 treats millisecond, microsecond and nanosecond of a Duration as its total length, the way arrow's date_part treats the other Duration parts. 2.4e9 microseconds don't fit in the Int32 result, so the result is NULL. That makes any Duration over about 35.8 minutes NULL for microsecond, and over about 24.8 days for millisecond.
On the dev list, @alamb agreed that NULL is fine for now and suggested following Postgres where we can. Postgres has no Duration type, so following it here means giving Duration the interval meaning: seconds within the minute, hours within the day. That would change every Duration part, not only the sub-second ones, because arrow's date_part returns the total for hour, minute and second as well. Should Duration parts take the interval meaning, or keep the total length?
Activity
gabotechs commentedon Feb 18, 2025
It can also be replicated with intervals:
gabotechs commentedon Feb 18, 2025
It seems like even without integer overflows, the overall logic is wrong:
Omega359 commentedon Feb 18, 2025
Hmm. I think this may be a separate issue as indeed it doesn't follow the typical pattern for that function where
should return 5 (the seconds element in the interval, not the overall seconds the interval covers)
alamb commentedon Mar 3, 2026
Appears to still be a problem
buraksenn commentedon Apr 13, 2026
I gave this a look and was able to fix it by casting duration array to nanosecond and then extracting components. However, it seemed like a fragile workaround so I was not sure of opening a PR.
Smth like this:
Should this be solved in https://github.com/apache/arrow-rs? Otherwise I can open a PR with the approach above
alamb commentedon Apr 13, 2026
Yes I think that is probably the right approach
Nisarg0007 commentedon Sep 9, 2026
I'd like to work on this. I reproduced the issue on v55.0.0:
This currently panics in debug builds at
date_part.rs:426withattempt to multiply with overflow; release builds silently wrap and return an incorrect value.I also confirmed that the interval-semantics portion of this issue was fixed by #14817, so I intend to target only the remaining
Durationpath.My current plan is to perform the subsecond calculation using
i64internally while preserving the existingInt32return type. For values outside theInt32range, should this return an error or saturate/clamp?@alamb — you previously suggested the duration-part work might belong in arrow-rs. My proposed change is limited to fixing the existing
i32arithmetic in DataFusion'sdate_part.rsrather than introducing an upstream kernel. Would a local fix be appropriate here?If this approach sounds good, I'd be happy to take this issue.
mbutrovich commentedon Oct 23, 2026
@Nisarg0007 are you still working on this?
Intervalalso drops the fraction pasti32::MAXnanoseconds, asDurationdoes:As I read it, this branch treats the overflowed nanoseconds as zero.
#26676 fixes the same overflow for
EXTRACT(EPOCH)indate_part.rsby computing from thei64value. Shouldsecond,millisecondandmicroseconddo the same?[-]date_part returning wrong results due to overflows[/-][+]`date_part` returning wrong results due to overflows[/+]mbutrovich commentedon Oct 24, 2026
#26686 removes the wrapped value and the debug panic, but it doesn't give the Postgres result for the reproducer:
In Postgres the difference is an interval, so
microsecondis the seconds field within the minute (0 here). In DataFusion it is aDuration, and #26686 treatsmillisecond,microsecondandnanosecondof aDurationas its total length, the way arrow'sdate_parttreats the otherDurationparts. 2.4e9 microseconds don't fit in theInt32result, so the result is NULL. That makes anyDurationover about 35.8 minutes NULL formicrosecond, and over about 24.8 days formillisecond.On the dev list, @alamb agreed that NULL is fine for now and suggested following Postgres where we can. Postgres has no
Durationtype, so following it here means givingDurationthe interval meaning: seconds within the minute, hours within the day. That would change everyDurationpart, not only the sub-second ones, because arrow'sdate_partreturns the total forhour,minuteandsecondas well. ShouldDurationparts take the interval meaning, or keep the total length?