1use std::cmp::Ordering;
4
5use rupg_common::{Error, Result, SqlState};
6use rupg_types::{
7 DATE_INFINITY, DATE_NEGATIVE_INFINITY, Interval, POSTGRES_EPOCH_JDATE, TIMESTAMP_INFINITY,
8 TIMESTAMP_NEGATIVE_INFINITY, TimeZone, UNIX_TO_POSTGRES_USECS, USECS_PER_DAY, USECS_PER_SEC,
9 Value, date2j, j2date,
10};
11
12use crate::cast::{
13 TIMESTAMP_END_JULIAN, infinite, local, midnight, timestamp_range, valid_timestamp,
14};
15use crate::compare::{compare, span};
16use crate::timezone::lookup_timezone;
17use crate::{Call, Kernel, bad_value};
18
19const DAYS_PER_MONTH: i32 = 30;
21const SECS_PER_DAY: i32 = 86_400;
23const MONTHS_PER_YEAR: i32 = 12;
25
26fn interval_range() -> Error {
27 Error::new(SqlState::DATETIME_VALUE_OUT_OF_RANGE, "interval out of range")
28}
29
30fn date_range() -> Error {
31 Error::new(SqlState::DATETIME_VALUE_OUT_OF_RANGE, "date out of range for timestamp")
32}
33
34pub(crate) fn is_nobegin(interval: &Interval) -> bool {
35 *interval == Interval::NEGATIVE_INFINITY
36}
37
38pub(crate) fn is_noend(interval: &Interval) -> bool {
39 *interval == Interval::INFINITY
40}
41
42fn not_finite(interval: &Interval) -> bool {
44 is_nobegin(interval) || is_noend(interval)
45}
46
47pub(crate) fn interval_arg(v: &Value) -> Result<Interval> {
48 let Value::Interval(interval) = v else { return Err(bad_value()) };
49 Ok(*interval)
50}
51
52pub(crate) fn timestamp_arg(v: &Value) -> Result<i64> {
53 match v {
54 Value::Timestamp(t) | Value::TimestampTz(t) => Ok(*t),
55 _ => Err(bad_value()),
56 }
57}
58
59fn float_arg(v: &Value) -> Result<f64> {
60 let Value::Float8(f) = v else { return Err(bad_value()) };
61 Ok(*f)
62}
63
64pub(crate) fn negate(interval: &Interval) -> Result<Interval> {
66 if is_nobegin(interval) {
67 return Ok(Interval::INFINITY);
68 }
69 if is_noend(interval) {
70 return Ok(Interval::NEGATIVE_INFINITY);
71 }
72 let result = Interval {
73 time: 0i64.checked_sub(interval.time).ok_or_else(interval_range)?,
74 day: 0i32.checked_sub(interval.day).ok_or_else(interval_range)?,
75 month: 0i32.checked_sub(interval.month).ok_or_else(interval_range)?,
76 };
77 if not_finite(&result) {
78 return Err(interval_range());
79 }
80 Ok(result)
81}
82
83pub(crate) fn interval_plus(a: &Interval, b: &Interval) -> Result<Interval> {
85 if is_nobegin(a) {
86 if is_noend(b) {
87 return Err(interval_range());
88 }
89 return Ok(Interval::NEGATIVE_INFINITY);
90 }
91 if is_noend(a) {
92 if is_nobegin(b) {
93 return Err(interval_range());
94 }
95 return Ok(Interval::INFINITY);
96 }
97 if not_finite(b) {
98 return Ok(*b);
99 }
100 let result = Interval {
101 month: a.month.checked_add(b.month).ok_or_else(interval_range)?,
102 day: a.day.checked_add(b.day).ok_or_else(interval_range)?,
103 time: a.time.checked_add(b.time).ok_or_else(interval_range)?,
104 };
105 if not_finite(&result) {
106 return Err(interval_range());
107 }
108 Ok(result)
109}
110
111pub(crate) fn interval_minus(a: &Interval, b: &Interval) -> Result<Interval> {
113 if is_nobegin(a) {
114 if is_nobegin(b) {
115 return Err(interval_range());
116 }
117 return Ok(Interval::NEGATIVE_INFINITY);
118 }
119 if is_noend(a) {
120 if is_noend(b) {
121 return Err(interval_range());
122 }
123 return Ok(Interval::INFINITY);
124 }
125 if is_nobegin(b) {
126 return Ok(Interval::INFINITY);
127 }
128 if is_noend(b) {
129 return Ok(Interval::NEGATIVE_INFINITY);
130 }
131 let result = Interval {
132 month: a.month.checked_sub(b.month).ok_or_else(interval_range)?,
133 day: a.day.checked_sub(b.day).ok_or_else(interval_range)?,
134 time: a.time.checked_sub(b.time).ok_or_else(interval_range)?,
135 };
136 if not_finite(&result) {
137 return Err(interval_range());
138 }
139 Ok(result)
140}
141
142fn interval_pl(_: &Call<'_>, args: &[Value]) -> Result<Value> {
143 let [a, b] = args else { return Err(bad_value()) };
144 interval_plus(&interval_arg(a)?, &interval_arg(b)?).map(Value::Interval)
145}
146
147fn interval_mi(_: &Call<'_>, args: &[Value]) -> Result<Value> {
148 let [a, b] = args else { return Err(bad_value()) };
149 interval_minus(&interval_arg(a)?, &interval_arg(b)?).map(Value::Interval)
150}
151
152fn interval_um(_: &Call<'_>, args: &[Value]) -> Result<Value> {
153 let [a] = args else { return Err(bad_value()) };
154 negate(&interval_arg(a)?).map(Value::Interval)
155}
156
157fn ts_round(x: f64) -> f64 {
159 (x * 1_000_000.0).round_ties_even() / 1_000_000.0
160}
161
162fn fits_i32(x: f64) -> bool {
164 x >= f64::from(i32::MIN) && x < -f64::from(i32::MIN)
165}
166
167#[allow(clippy::cast_precision_loss)]
169fn fits_i64(x: f64) -> bool {
170 x >= i64::MIN as f64 && x < -(i64::MIN as f64)
171}
172
173#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
175fn scale(interval: &Interval, op: impl Fn(f64) -> f64) -> Result<Interval> {
176 let months = op(f64::from(interval.month));
177 if months.is_nan() || !fits_i32(months) {
178 return Err(interval_range());
179 }
180 let month = months as i32;
181 let days = op(f64::from(interval.day));
182 if days.is_nan() || !fits_i32(days) {
183 return Err(interval_range());
184 }
185 let mut day = days as i32;
186 let month_remainder_days = ts_round((months - f64::from(month)) * f64::from(DAYS_PER_MONTH));
187 let mut sec_remainder = ts_round(
188 (days - f64::from(day) + month_remainder_days - f64::from(month_remainder_days as i32))
189 * f64::from(SECS_PER_DAY),
190 );
191 if sec_remainder.abs() >= f64::from(SECS_PER_DAY) {
192 let whole = (sec_remainder / f64::from(SECS_PER_DAY)) as i32;
193 day = day.checked_add(whole).ok_or_else(interval_range)?;
194 sec_remainder -= f64::from(whole * SECS_PER_DAY);
195 }
196 day = day.checked_add(month_remainder_days as i32).ok_or_else(interval_range)?;
197 let time = (op(interval.time as f64) + sec_remainder * USECS_PER_SEC as f64).round_ties_even();
198 if time.is_nan() || !fits_i64(time) {
199 return Err(interval_range());
200 }
201 let result = Interval { time: time as i64, day, month };
202 if not_finite(&result) {
203 return Err(interval_range());
204 }
205 Ok(result)
206}
207
208fn interval_times(interval: &Interval, factor: f64) -> Result<Interval> {
210 if factor.is_nan() {
211 return Err(interval_range());
212 }
213 if not_finite(interval) {
214 if factor == 0.0 {
215 return Err(interval_range());
216 }
217 return if factor < 0.0 { negate(interval) } else { Ok(*interval) };
218 }
219 if factor.is_infinite() {
220 let sign = match span(interval).cmp(&0) {
221 Ordering::Less => -1.0,
222 Ordering::Equal => return Err(interval_range()),
223 Ordering::Greater => 1.0,
224 };
225 return Ok(if factor * sign < 0.0 {
226 Interval::NEGATIVE_INFINITY
227 } else {
228 Interval::INFINITY
229 });
230 }
231 scale(interval, |x| x * factor)
232}
233
234#[derive(Clone, Debug)]
236pub struct IntervalSum {
237 count: i64,
238 sum: Interval,
239 pinf: i64,
240 ninf: i64,
241}
242
243impl Default for IntervalSum {
244 fn default() -> Self {
245 IntervalSum { count: 0, sum: Interval { time: 0, day: 0, month: 0 }, pinf: 0, ninf: 0 }
246 }
247}
248
249impl IntervalSum {
250 pub fn add(&mut self, value: &Interval) -> Result<()> {
252 if is_nobegin(value) {
253 self.ninf += 1;
254 } else if is_noend(value) {
255 self.pinf += 1;
256 } else {
257 self.sum = interval_plus(&self.sum, value)?;
258 self.count += 1;
259 }
260 Ok(())
261 }
262
263 fn infinite(&self) -> Result<Option<Interval>> {
265 match (self.pinf > 0, self.ninf > 0) {
266 (true, true) => Err(interval_range()),
267 (true, false) => Ok(Some(Interval::INFINITY)),
268 (false, true) => Ok(Some(Interval::NEGATIVE_INFINITY)),
269 (false, false) => Ok(None),
270 }
271 }
272
273 pub fn sum(&self) -> Result<Option<Interval>> {
275 if self.count + self.pinf + self.ninf == 0 {
276 return Ok(None);
277 }
278 Ok(Some(self.infinite()?.unwrap_or(self.sum)))
279 }
280
281 pub fn avg(&self) -> Result<Option<Interval>> {
283 if self.count + self.pinf + self.ninf == 0 {
284 return Ok(None);
285 }
286 if let Some(infinite) = self.infinite()? {
287 return Ok(Some(infinite));
288 }
289 #[allow(clippy::cast_precision_loss)]
290 let count = self.count as f64;
291 scale(&self.sum, |x| x / count).map(Some)
292 }
293}
294
295pub fn interval_lerp(lo: &Interval, hi: &Interval, pct: f64) -> Result<Interval> {
297 interval_plus(&interval_times(&interval_minus(hi, lo)?, pct)?, lo)
298}
299
300fn interval_mul(_: &Call<'_>, args: &[Value]) -> Result<Value> {
301 let [a, b] = args else { return Err(bad_value()) };
302 interval_times(&interval_arg(a)?, float_arg(b)?).map(Value::Interval)
303}
304
305fn mul_d_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
306 let [a, b] = args else { return Err(bad_value()) };
307 interval_times(&interval_arg(b)?, float_arg(a)?).map(Value::Interval)
308}
309
310fn interval_div(_: &Call<'_>, args: &[Value]) -> Result<Value> {
311 let [a, b] = args else { return Err(bad_value()) };
312 let (interval, factor) = (interval_arg(a)?, float_arg(b)?);
313 if factor == 0.0 {
314 return Err(Error::new(SqlState::DIVISION_BY_ZERO, "division by zero"));
315 }
316 if factor.is_nan() {
317 return Err(interval_range());
318 }
319 if not_finite(&interval) {
320 if factor.is_infinite() {
321 return Err(interval_range());
322 }
323 let result = if factor < 0.0 { negate(&interval)? } else { interval };
324 return Ok(Value::Interval(result));
325 }
326 scale(&interval, |x| x / factor).map(Value::Interval)
327}
328
329fn justify_hours(interval: &Interval) -> Result<Interval> {
331 if not_finite(interval) {
332 return Ok(*interval);
333 }
334 let mut result = *interval;
335 let whole = result.time / USECS_PER_DAY;
336 result.time -= whole * USECS_PER_DAY;
337 let whole = i32::try_from(whole).map_err(|_| interval_range())?;
338 result.day = result.day.checked_add(whole).ok_or_else(interval_range)?;
339 if result.day > 0 && result.time < 0 {
340 result.time += USECS_PER_DAY;
341 result.day -= 1;
342 } else if result.day < 0 && result.time > 0 {
343 result.time -= USECS_PER_DAY;
344 result.day += 1;
345 }
346 Ok(result)
347}
348
349fn justify_days(interval: &Interval) -> Result<Interval> {
351 if not_finite(interval) {
352 return Ok(*interval);
353 }
354 let mut result = *interval;
355 let whole = result.day / DAYS_PER_MONTH;
356 result.day -= whole * DAYS_PER_MONTH;
357 result.month = result.month.checked_add(whole).ok_or_else(interval_range)?;
358 if result.month > 0 && result.day < 0 {
359 result.day += DAYS_PER_MONTH;
360 result.month -= 1;
361 } else if result.month < 0 && result.day > 0 {
362 result.day -= DAYS_PER_MONTH;
363 result.month += 1;
364 }
365 Ok(result)
366}
367
368#[allow(clippy::cast_possible_truncation)]
370fn justify_interval(interval: &Interval) -> Result<Interval> {
371 if not_finite(interval) {
372 return Ok(*interval);
373 }
374 let mut result = *interval;
375 if (result.day > 0 && result.time > 0) || (result.day < 0 && result.time < 0) {
376 let whole = result.day / DAYS_PER_MONTH;
377 result.day -= whole * DAYS_PER_MONTH;
378 result.month = result.month.checked_add(whole).ok_or_else(interval_range)?;
379 }
380 let whole = result.time / USECS_PER_DAY;
381 result.time -= whole * USECS_PER_DAY;
382 result.day += whole as i32;
383 let whole = result.day / DAYS_PER_MONTH;
384 result.day -= whole * DAYS_PER_MONTH;
385 result.month = result.month.checked_add(whole).ok_or_else(interval_range)?;
386 if result.month > 0 && (result.day < 0 || (result.day == 0 && result.time < 0)) {
387 result.day += DAYS_PER_MONTH;
388 result.month -= 1;
389 } else if result.month < 0 && (result.day > 0 || (result.day == 0 && result.time > 0)) {
390 result.day -= DAYS_PER_MONTH;
391 result.month += 1;
392 }
393 if result.day > 0 && result.time < 0 {
394 result.time += USECS_PER_DAY;
395 result.day -= 1;
396 } else if result.day < 0 && result.time > 0 {
397 result.time -= USECS_PER_DAY;
398 result.day += 1;
399 }
400 Ok(result)
401}
402
403fn interval_justify_hours(_: &Call<'_>, args: &[Value]) -> Result<Value> {
404 let [a] = args else { return Err(bad_value()) };
405 justify_hours(&interval_arg(a)?).map(Value::Interval)
406}
407
408fn interval_justify_days(_: &Call<'_>, args: &[Value]) -> Result<Value> {
409 let [a] = args else { return Err(bad_value()) };
410 justify_days(&interval_arg(a)?).map(Value::Interval)
411}
412
413fn interval_justify_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
414 let [a] = args else { return Err(bad_value()) };
415 justify_interval(&interval_arg(a)?).map(Value::Interval)
416}
417
418pub(crate) struct Tm {
420 pub(crate) year: i32,
421 pub(crate) mon: i32,
422 pub(crate) mday: i32,
423 pub(crate) time: i64,
425}
426
427fn is_leap(year: i32) -> bool {
429 year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
430}
431
432pub(crate) fn days_of_month(year: i32, mon: i32) -> i32 {
434 const DAYS: [i32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
435 if mon == 2 && is_leap(year) { 29 } else { DAYS[(mon - 1) as usize] }
436}
437
438impl Tm {
439 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
441 pub(crate) fn of(ts: i64) -> Result<Tm> {
442 let julian = ts.div_euclid(USECS_PER_DAY) + i64::from(POSTGRES_EPOCH_JDATE);
443 let julian = i32::try_from(julian).ok().filter(|&j| j >= 0).ok_or_else(timestamp_range)?;
444 let (year, mon, mday) = j2date(julian);
445 Ok(Tm { year, mon: mon as i32, mday: mday as i32, time: ts.rem_euclid(USECS_PER_DAY) })
446 }
447
448 fn of_local(call: &Call<'_>, ts: i64) -> Result<Tm> {
450 Tm::of(ts)?;
451 Tm::of(local(call.session, ts)?)
452 }
453
454 fn of_zone(zone: &dyn TimeZone, ts: i64) -> Result<Tm> {
456 Tm::of(ts)?;
457 let unix = ts.div_euclid(USECS_PER_SEC) - UNIX_TO_POSTGRES_USECS / USECS_PER_SEC;
458 let east = i64::from(zone.at(unix).0);
459 Tm::of(ts.checked_add(east * USECS_PER_SEC).ok_or_else(timestamp_range)?)
460 }
461
462 pub(crate) fn timestamp(&self, zone: Option<&dyn TimeZone>) -> Result<i64> {
464 let local = self.local()?;
465 let west = zone.map_or(0, |zone| {
466 let seconds = local.div_euclid(USECS_PER_SEC) - UNIX_TO_POSTGRES_USECS / USECS_PER_SEC;
467 rupg_types::local_offset(seconds, zone).0
468 });
469 Tm::shift(local, west)
470 }
471
472 pub(crate) fn timestamp_west(&self, west: i32) -> Result<i64> {
474 Tm::shift(self.local()?, west)
475 }
476
477 fn local(&self) -> Result<i64> {
479 let valid = (self.year > -4713 || (self.year == -4713 && self.mon >= 11))
480 && (self.year < 5_874_898 || (self.year == 5_874_898 && self.mon < 6));
481 if !valid {
482 return Err(timestamp_range());
483 }
484 let date = i64::from(date2j(self.year, self.mon, self.mday) - POSTGRES_EPOCH_JDATE);
485 date.checked_mul(USECS_PER_DAY)
486 .and_then(|t| t.checked_add(self.time))
487 .ok_or_else(timestamp_range)
488 }
489
490 fn shift(local: i64, west: i32) -> Result<i64> {
492 let result = local.wrapping_add(i64::from(west) * USECS_PER_SEC);
493 if !valid_timestamp(result) {
494 return Err(timestamp_range());
495 }
496 Ok(result)
497 }
498}
499
500pub(crate) fn timestamp_plus(
502 ts: i64,
503 interval: &Interval,
504 zone: Option<&dyn TimeZone>,
505) -> Result<i64> {
506 if is_nobegin(interval) {
507 if ts == TIMESTAMP_INFINITY {
508 return Err(timestamp_range());
509 }
510 return Ok(TIMESTAMP_NEGATIVE_INFINITY);
511 }
512 if is_noend(interval) {
513 if ts == TIMESTAMP_NEGATIVE_INFINITY {
514 return Err(timestamp_range());
515 }
516 return Ok(TIMESTAMP_INFINITY);
517 }
518 if infinite(ts) {
519 return Ok(ts);
520 }
521 let fields = |ts: i64| match zone {
522 Some(zone) => Tm::of_zone(zone, ts),
523 None => Tm::of(ts),
524 };
525 let mut ts = ts;
526 if interval.month != 0 {
527 let mut tm = fields(ts)?;
528 tm.mon = tm.mon.checked_add(interval.month).ok_or_else(timestamp_range)?;
529 if tm.mon > MONTHS_PER_YEAR {
530 tm.year = tm.year.wrapping_add((tm.mon - 1) / MONTHS_PER_YEAR);
531 tm.mon = (tm.mon - 1) % MONTHS_PER_YEAR + 1;
532 } else if tm.mon < 1 {
533 tm.year = tm.year.wrapping_add(tm.mon / MONTHS_PER_YEAR - 1);
534 tm.mon = tm.mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
535 }
536 tm.mday = tm.mday.min(days_of_month(tm.year, tm.mon));
537 ts = tm.timestamp(zone)?;
538 }
539 if interval.day != 0 {
540 let mut tm = fields(ts)?;
541 let lowest = if zone.is_some() { -1 } else { 0 };
542 let julian = date2j(tm.year, tm.mon, tm.mday)
543 .checked_add(interval.day)
544 .filter(|&j| j >= lowest)
545 .ok_or_else(timestamp_range)?;
546 let (year, mon, mday) = j2date(julian);
547 (tm.year, tm.mon, tm.mday) = (year, mon as i32, mday as i32);
548 ts = tm.timestamp(zone)?;
549 }
550 let ts = ts.checked_add(interval.time).ok_or_else(timestamp_range)?;
551 if !valid_timestamp(ts) {
552 return Err(timestamp_range());
553 }
554 Ok(ts)
555}
556
557fn timestamp_pl_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
558 let [a, b] = args else { return Err(bad_value()) };
559 timestamp_plus(timestamp_arg(a)?, &interval_arg(b)?, None).map(Value::Timestamp)
560}
561
562fn timestamp_mi_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
563 let [a, b] = args else { return Err(bad_value()) };
564 timestamp_plus(timestamp_arg(a)?, &negate(&interval_arg(b)?)?, None).map(Value::Timestamp)
565}
566
567fn timestamptz_pl_interval(call: &Call<'_>, args: &[Value]) -> Result<Value> {
568 let [a, b] = args else { return Err(bad_value()) };
569 let zone = call.session.zone()?;
570 timestamp_plus(timestamp_arg(a)?, &interval_arg(b)?, Some(zone.as_ref()))
571 .map(Value::TimestampTz)
572}
573
574fn timestamptz_mi_interval(call: &Call<'_>, args: &[Value]) -> Result<Value> {
575 let [a, b] = args else { return Err(bad_value()) };
576 let interval = negate(&interval_arg(b)?)?;
577 let zone = call.session.zone()?;
578 timestamp_plus(timestamp_arg(a)?, &interval, Some(zone.as_ref())).map(Value::TimestampTz)
579}
580
581fn timestamptz_pl_interval_at_zone(call: &Call<'_>, args: &[Value]) -> Result<Value> {
583 let [a, b, zone] = args else { return Err(bad_value()) };
584 let zone = lookup_timezone(call, zone)?;
585 timestamp_plus(timestamp_arg(a)?, &interval_arg(b)?, Some(zone.as_ref()))
586 .map(Value::TimestampTz)
587}
588
589fn timestamptz_mi_interval_at_zone(call: &Call<'_>, args: &[Value]) -> Result<Value> {
591 let [a, b, zone] = args else { return Err(bad_value()) };
592 let interval = negate(&interval_arg(b)?)?;
593 let zone = lookup_timezone(call, zone)?;
594 timestamp_plus(timestamp_arg(a)?, &interval, Some(zone.as_ref())).map(Value::TimestampTz)
595}
596
597fn timestamp_mi(_: &Call<'_>, args: &[Value]) -> Result<Value> {
599 let [a, b] = args else { return Err(bad_value()) };
600 let (a, b) = (timestamp_arg(a)?, timestamp_arg(b)?);
601 if infinite(a) || infinite(b) {
602 let result = match (a, b) {
603 (TIMESTAMP_NEGATIVE_INFINITY, TIMESTAMP_NEGATIVE_INFINITY)
604 | (TIMESTAMP_INFINITY, TIMESTAMP_INFINITY) => return Err(interval_range()),
605 (TIMESTAMP_NEGATIVE_INFINITY, _) | (_, TIMESTAMP_INFINITY) => {
606 Interval::NEGATIVE_INFINITY
607 }
608 _ => Interval::INFINITY,
609 };
610 return Ok(Value::Interval(result));
611 }
612 let time = a.checked_sub(b).ok_or_else(interval_range)?;
613 justify_hours(&Interval { time, day: 0, month: 0 }).map(Value::Interval)
614}
615
616fn age(a: i64, b: i64, call: Option<&Call<'_>>) -> Result<Interval> {
618 match (a, b) {
619 (TIMESTAMP_NEGATIVE_INFINITY, TIMESTAMP_NEGATIVE_INFINITY)
620 | (TIMESTAMP_INFINITY, TIMESTAMP_INFINITY) => return Err(interval_range()),
621 (TIMESTAMP_NEGATIVE_INFINITY, _) | (_, TIMESTAMP_INFINITY) => {
622 return Ok(Interval::NEGATIVE_INFINITY);
623 }
624 (TIMESTAMP_INFINITY, _) | (_, TIMESTAMP_NEGATIVE_INFINITY) => {
625 return Ok(Interval::INFINITY);
626 }
627 _ => {}
628 }
629 let fields = |ts: i64| match call {
630 Some(call) => Tm::of_local(call, ts),
631 None => Tm::of(ts),
632 };
633 let (tm1, tm2) = (fields(a)?, fields(b)?);
634 let split = |tm: &Tm| {
635 let seconds = tm.time / USECS_PER_SEC;
636 [tm.time % USECS_PER_SEC, seconds % 60, seconds / 60 % 60, seconds / 3600]
637 };
638 let (t1, t2) = (split(&tm1), split(&tm2));
639 let sign = if a < b { -1 } else { 1 };
640 let mut usec = sign * (t1[0] - t2[0]);
641 let mut sec = sign * (t1[1] - t2[1]);
642 let mut min = sign * (t1[2] - t2[2]);
643 let mut hour = sign * (t1[3] - t2[3]);
644 let mut mday = sign * i64::from(tm1.mday - tm2.mday);
645 let mut mon = sign * i64::from(tm1.mon - tm2.mon);
646 let mut year = sign * (i64::from(tm1.year) - i64::from(tm2.year));
647 while usec < 0 {
648 usec += USECS_PER_SEC;
649 sec -= 1;
650 }
651 while sec < 0 {
652 sec += 60;
653 min -= 1;
654 }
655 while min < 0 {
656 min += 60;
657 hour -= 1;
658 }
659 while hour < 0 {
660 hour += 24;
661 mday -= 1;
662 }
663 let borrow = if a < b { &tm1 } else { &tm2 };
664 while mday < 0 {
665 mday += i64::from(days_of_month(borrow.year, borrow.mon));
666 mon -= 1;
667 }
668 while mon < 0 {
669 mon += i64::from(MONTHS_PER_YEAR);
670 year -= 1;
671 }
672 let months = sign * (year * i64::from(MONTHS_PER_YEAR) + mon);
673 let month = i32::try_from(months).map_err(|_| interval_range())?;
674 let day = i32::try_from(sign * mday).map_err(|_| interval_range())?;
675 let time = (sign * hour)
676 .checked_mul(3600 * USECS_PER_SEC)
677 .and_then(|t| t.checked_add(sign * min * 60 * USECS_PER_SEC))
678 .and_then(|t| t.checked_add(sign * sec * USECS_PER_SEC))
679 .and_then(|t| t.checked_add(sign * usec))
680 .ok_or_else(interval_range)?;
681 let result = Interval { time, day, month };
682 if not_finite(&result) {
683 return Err(interval_range());
684 }
685 Ok(result)
686}
687
688fn timestamp_age(_: &Call<'_>, args: &[Value]) -> Result<Value> {
689 let [a, b] = args else { return Err(bad_value()) };
690 age(timestamp_arg(a)?, timestamp_arg(b)?, None).map(Value::Interval)
691}
692
693fn timestamptz_age(call: &Call<'_>, args: &[Value]) -> Result<Value> {
694 let [a, b] = args else { return Err(bad_value()) };
695 age(timestamp_arg(a)?, timestamp_arg(b)?, Some(call)).map(Value::Interval)
696}
697
698fn wrap_time(time: i64, add: i64) -> i64 {
700 time.wrapping_add(add).rem_euclid(USECS_PER_DAY)
701}
702
703fn infinite_time(add: bool) -> Error {
704 let message = if add {
705 "cannot add infinite interval to time"
706 } else {
707 "cannot subtract infinite interval from time"
708 };
709 Error::new(SqlState::DATETIME_VALUE_OUT_OF_RANGE, message)
710}
711
712fn time_pl_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
713 let [Value::Time(t), b] = args else { return Err(bad_value()) };
714 let interval = interval_arg(b)?;
715 if not_finite(&interval) {
716 return Err(infinite_time(true));
717 }
718 Ok(Value::Time(wrap_time(*t, interval.time)))
719}
720
721fn time_mi_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
722 let [Value::Time(t), b] = args else { return Err(bad_value()) };
723 let interval = interval_arg(b)?;
724 if not_finite(&interval) {
725 return Err(infinite_time(false));
726 }
727 Ok(Value::Time(wrap_time(*t, interval.time.wrapping_neg())))
728}
729
730fn timetz_pl_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
731 let [Value::TimeTz(t, zone), b] = args else { return Err(bad_value()) };
732 let interval = interval_arg(b)?;
733 if not_finite(&interval) {
734 return Err(infinite_time(true));
735 }
736 Ok(Value::TimeTz(wrap_time(*t, interval.time), *zone))
737}
738
739fn timetz_mi_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
740 let [Value::TimeTz(t, zone), b] = args else { return Err(bad_value()) };
741 let interval = interval_arg(b)?;
742 if not_finite(&interval) {
743 return Err(infinite_time(false));
744 }
745 Ok(Value::TimeTz(wrap_time(*t, interval.time.wrapping_neg()), *zone))
746}
747
748fn time_mi_time(_: &Call<'_>, args: &[Value]) -> Result<Value> {
749 let [Value::Time(a), Value::Time(b)] = args else { return Err(bad_value()) };
750 Ok(Value::Interval(Interval { time: a - b, day: 0, month: 0 }))
751}
752
753fn interval_time(_: &Call<'_>, args: &[Value]) -> Result<Value> {
755 let [a] = args else { return Err(bad_value()) };
756 let interval = interval_arg(a)?;
757 if not_finite(&interval) {
758 return Err(Error::new(
759 SqlState::DATETIME_VALUE_OUT_OF_RANGE,
760 "cannot convert infinite interval to time",
761 ));
762 }
763 Ok(Value::Time(interval.time.rem_euclid(USECS_PER_DAY)))
764}
765
766fn time_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
767 let [Value::Time(t)] = args else { return Err(bad_value()) };
768 Ok(Value::Interval(Interval { time: *t, day: 0, month: 0 }))
769}
770
771fn date_pl_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
772 let [Value::Date(d), b] = args else { return Err(bad_value()) };
773 timestamp_plus(midnight(*d)?, &interval_arg(b)?, None).map(Value::Timestamp)
774}
775
776fn date_mi_interval(_: &Call<'_>, args: &[Value]) -> Result<Value> {
777 let [Value::Date(d), b] = args else { return Err(bad_value()) };
778 timestamp_plus(midnight(*d)?, &negate(&interval_arg(b)?)?, None).map(Value::Timestamp)
779}
780
781fn datetime_timestamp(_: &Call<'_>, args: &[Value]) -> Result<Value> {
783 let [Value::Date(d), Value::Time(t)] = args else { return Err(bad_value()) };
784 let result = midnight(*d)?;
785 if infinite(result) {
786 return Ok(Value::Timestamp(result));
787 }
788 let result = result + t;
789 if !valid_timestamp(result) {
790 return Err(timestamp_range());
791 }
792 Ok(Value::Timestamp(result))
793}
794
795fn datetimetz_timestamptz(_: &Call<'_>, args: &[Value]) -> Result<Value> {
797 let [Value::Date(d), Value::TimeTz(t, zone)] = args else { return Err(bad_value()) };
798 match *d {
799 DATE_NEGATIVE_INFINITY => return Ok(Value::TimestampTz(TIMESTAMP_NEGATIVE_INFINITY)),
800 DATE_INFINITY => return Ok(Value::TimestampTz(TIMESTAMP_INFINITY)),
801 d if d >= TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE => return Err(date_range()),
802 _ => {}
803 }
804 let result = i64::from(*d) * USECS_PER_DAY + t + i64::from(*zone) * USECS_PER_SEC;
805 if !valid_timestamp(result) {
806 return Err(date_range());
807 }
808 Ok(Value::TimestampTz(result))
809}
810
811fn overlaps(_: &Call<'_>, args: &[Value]) -> Result<Value> {
813 let [s1, e1, s2, e2] = args else { return Err(bad_value()) };
814 let order = |a: &Value, b: &Value| compare(0, a, 0, b);
815 fn ends<'a>(
817 s: &'a Value,
818 e: &'a Value,
819 order: &impl Fn(&Value, &Value) -> Result<Ordering>,
820 ) -> Result<Option<(&'a Value, Option<&'a Value>)>> {
821 Ok(match (s.is_null(), e.is_null()) {
822 (true, true) => None,
823 (true, false) => Some((e, None)),
824 (false, true) => Some((s, None)),
825 (false, false) if order(s, e)? == Ordering::Greater => Some((e, Some(s))),
826 (false, false) => Some((s, Some(e))),
827 })
828 }
829 let Some((s1, e1)) = ends(s1, e1, &order)? else { return Ok(Value::Null) };
830 let Some((s2, e2)) = ends(s2, e2, &order)? else { return Ok(Value::Null) };
831 let later = |start: &Value, end: Option<&Value>, other_end: Option<&Value>| -> Result<Value> {
833 let Some(end) = end else { return Ok(Value::Null) };
834 if order(start, end)? == Ordering::Less {
835 return Ok(Value::Bool(true));
836 }
837 Ok(if other_end.is_none() { Value::Null } else { Value::Bool(false) })
838 };
839 match order(s1, s2)? {
840 Ordering::Greater => later(s1, e2, e1),
841 Ordering::Less => later(s2, e1, e2),
842 Ordering::Equal => {
843 Ok(if e1.is_none() || e2.is_none() { Value::Null } else { Value::Bool(true) })
844 }
845 }
846}
847
848pub(crate) fn by_src(src: &str) -> Option<Kernel> {
850 Some(match src {
851 "interval_pl" => interval_pl,
852 "interval_mi" => interval_mi,
853 "interval_um" => interval_um,
854 "interval_mul" => interval_mul,
855 "mul_d_interval" => mul_d_interval,
856 "interval_div" => interval_div,
857 "interval_justify_hours" => interval_justify_hours,
858 "interval_justify_days" => interval_justify_days,
859 "interval_justify_interval" => interval_justify_interval,
860 "timestamp_pl_interval" => timestamp_pl_interval,
861 "timestamp_mi_interval" => timestamp_mi_interval,
862 "timestamptz_pl_interval" => timestamptz_pl_interval,
863 "timestamptz_mi_interval" => timestamptz_mi_interval,
864 "timestamptz_pl_interval_at_zone" => timestamptz_pl_interval_at_zone,
865 "timestamptz_mi_interval_at_zone" => timestamptz_mi_interval_at_zone,
866 "timestamp_mi" => timestamp_mi,
867 "timestamp_age" => timestamp_age,
868 "timestamptz_age" => timestamptz_age,
869 "time_pl_interval" => time_pl_interval,
870 "time_mi_interval" => time_mi_interval,
871 "timetz_pl_interval" => timetz_pl_interval,
872 "timetz_mi_interval" => timetz_mi_interval,
873 "time_mi_time" => time_mi_time,
874 "interval_time" => interval_time,
875 "time_interval" => time_interval,
876 "date_pl_interval" => date_pl_interval,
877 "date_mi_interval" => date_mi_interval,
878 "datetime_timestamp" => datetime_timestamp,
879 "datetimetz_timestamptz" => datetimetz_timestamptz,
880 "overlaps_timestamp" | "overlaps_time" | "overlaps_timetz" => overlaps,
881 _ => return None,
882 })
883}