1use std::cmp::Ordering;
10
11use uqa_core::{
12 memory::{Produced, ProductionControl},
13 TemporalValue, Value,
14};
15
16mod production;
17mod relationships;
18pub(super) use production::{
19 canonical_multirange_text_with_control, canonical_range_as_multirange_text_with_control,
20 canonical_range_text_with_control, multirange_from_produced_ranges,
21 parse_multirange_with_control, parse_range_with_control,
22};
23
24use crate::ast::RangeSubtype;
25use crate::error::Result;
26use crate::SQLError;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct CanonicalRange {
30 subtype: RangeSubtype,
31 lower: Option<Value>,
32 upper: Option<Value>,
33 lower_inclusive: bool,
34 upper_inclusive: bool,
35 empty: bool,
36}
37
38impl CanonicalRange {
39 fn empty(subtype: RangeSubtype) -> Self {
40 Self {
41 subtype,
42 lower: None,
43 upper: None,
44 lower_inclusive: false,
45 upper_inclusive: false,
46 empty: true,
47 }
48 }
49
50 #[must_use]
51 pub const fn subtype(&self) -> RangeSubtype {
52 self.subtype
53 }
54
55 #[must_use]
56 pub const fn is_empty(&self) -> bool {
57 self.empty
58 }
59
60 #[must_use]
61 pub const fn lower_inclusive(&self) -> bool {
62 self.lower_inclusive
63 }
64
65 #[must_use]
66 pub const fn upper_inclusive(&self) -> bool {
67 self.upper_inclusive
68 }
69
70 #[must_use]
71 pub fn lower(&self) -> Option<&Value> {
72 self.lower.as_ref()
73 }
74
75 #[must_use]
76 pub fn upper(&self) -> Option<&Value> {
77 self.upper.as_ref()
78 }
79
80 #[must_use]
81 pub fn overlaps(&self, other: &Self) -> bool {
82 self.overlaps_with_control(other, &ProductionControl::uncontrolled())
83 .expect("ordinary range overlap")
84 }
85
86 #[must_use]
87 pub fn adjacent(&self, other: &Self) -> bool {
88 self.adjacent_with_control(other, &ProductionControl::uncontrolled())
89 .expect("ordinary range adjacency")
90 }
91
92 #[must_use]
93 pub fn contains_range(&self, other: &Self) -> bool {
94 self.contains_range_with_control(other, &ProductionControl::uncontrolled())
95 .expect("ordinary range containment")
96 }
97
98 #[must_use]
99 pub fn contains_value(&self, value: &Value) -> bool {
100 self.contains_value_with_control(value, &ProductionControl::uncontrolled())
101 .expect("ordinary range element containment")
102 }
103
104 #[must_use]
107 pub fn merge_cover(&self, other: &Self) -> Self {
108 self.merge_cover_with_control(other, &ProductionControl::uncontrolled())
109 .expect("ordinary range cover")
110 .into_uncontrolled()
111 .expect("ordinary range")
112 }
113
114 pub(super) fn to_text_with_control(
115 &self,
116 control: &ProductionControl<'_>,
117 ) -> Result<Produced<String>> {
118 production::range_text(self, control)
119 }
120
121 #[must_use]
122 pub fn to_text(&self) -> String {
123 production::range_text(self, &ProductionControl::uncontrolled())
124 .expect("ordinary range formatting")
125 .into_uncontrolled()
126 .expect("ordinary range text")
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct CanonicalMultirange {
132 subtype: RangeSubtype,
133 ranges: Vec<CanonicalRange>,
134}
135
136impl CanonicalMultirange {
137 #[must_use]
138 pub fn ranges(&self) -> &[CanonicalRange] {
139 &self.ranges
140 }
141
142 #[must_use]
143 pub fn is_empty(&self) -> bool {
144 self.ranges.is_empty()
145 }
146
147 #[must_use]
148 pub fn contains_range(&self, range: &CanonicalRange) -> bool {
149 range.subtype == self.subtype
150 && (range.empty || self.ranges.iter().any(|item| item.contains_range(range)))
151 }
152
153 #[must_use]
154 pub fn contains_multirange(&self, other: &Self) -> bool {
155 self.subtype == other.subtype && other.ranges.iter().all(|range| self.contains_range(range))
156 }
157
158 #[must_use]
159 pub fn overlaps_range(&self, range: &CanonicalRange) -> bool {
160 range.subtype == self.subtype && self.ranges.iter().any(|item| item.overlaps(range))
161 }
162
163 #[must_use]
164 pub fn overlaps_multirange(&self, other: &Self) -> bool {
165 self.subtype == other.subtype
166 && self
167 .ranges
168 .iter()
169 .any(|left| other.ranges.iter().any(|right| left.overlaps(right)))
170 }
171
172 #[must_use]
173 pub fn merge_cover(&self) -> CanonicalRange {
174 self.merge_cover_with_control(&ProductionControl::uncontrolled())
175 .expect("ordinary multirange cover")
176 .into_uncontrolled()
177 .expect("ordinary range")
178 }
179
180 pub(super) fn to_text_with_control(
181 &self,
182 control: &ProductionControl<'_>,
183 ) -> Result<Produced<String>> {
184 production::multirange_text(self, control)
185 }
186
187 #[must_use]
188 pub fn to_text(&self) -> String {
189 production::multirange_text(self, &ProductionControl::uncontrolled())
190 .expect("ordinary multirange formatting")
191 .into_uncontrolled()
192 .expect("ordinary multirange text")
193 }
194}
195
196pub fn parse_range(text: &str, subtype: RangeSubtype) -> Result<CanonicalRange> {
197 Ok(
198 production::parse_range_with_control(text, subtype, &ProductionControl::uncontrolled())?
199 .into_uncontrolled()
200 .expect("ordinary range"),
201 )
202}
203
204pub fn parse_multirange(text: &str, subtype: RangeSubtype) -> Result<CanonicalMultirange> {
205 Ok(production::parse_multirange_with_control(
206 text,
207 subtype,
208 &ProductionControl::uncontrolled(),
209 )?
210 .into_uncontrolled()
211 .expect("ordinary multirange"))
212}
213
214pub fn multirange_from_ranges(
215 subtype: RangeSubtype,
216 ranges: impl IntoIterator<Item = CanonicalRange>,
217) -> CanonicalMultirange {
218 production::normalize_ranges(ranges, subtype, &ProductionControl::uncontrolled(), None)
219 .expect("ordinary multirange normalization")
220 .into_uncontrolled()
221 .expect("ordinary multirange")
222}
223
224fn increment_discrete(value: &Value, subtype: RangeSubtype) -> Result<Value> {
225 match (subtype, value) {
226 (RangeSubtype::Integer, Value::Int(value)) => i32::try_from(*value)
227 .ok()
228 .and_then(|value| value.checked_add(1))
229 .map(|value| Value::Int(i64::from(value)))
230 .ok_or_else(|| range_overflow("integer")),
231 (RangeSubtype::BigInteger, Value::Int(value)) => value
232 .checked_add(1)
233 .map(Value::Int)
234 .ok_or_else(|| range_overflow("bigint")),
235 (RangeSubtype::Date, Value::Temporal(TemporalValue::Date { days })) => days
236 .checked_add(1)
237 .map(|days| Value::Temporal(TemporalValue::Date { days }))
238 .ok_or_else(|| range_overflow("date")),
239 _ => Err(SQLError::Internal(format!(
240 "range subtype {subtype:?} received incompatible bound {value:?}"
241 ))),
242 }
243}
244
245fn is_discrete(subtype: RangeSubtype) -> bool {
246 matches!(
247 subtype,
248 RangeSubtype::Integer | RangeSubtype::BigInteger | RangeSubtype::Date
249 )
250}
251
252fn invalid_range(text: &str, subtype: RangeSubtype) -> SQLError {
253 SQLError::Routine {
254 sqlstate: "22P02".into(),
255 message: format!(
256 "malformed range literal: \"{text}\" for type {}",
257 subtype.range_name()
258 ),
259 }
260}
261
262fn invalid_multirange(text: &str, subtype: RangeSubtype) -> SQLError {
263 SQLError::Routine {
264 sqlstate: "22P02".into(),
265 message: format!(
266 "malformed multirange literal: \"{text}\" for type {}",
267 subtype.multirange_name()
268 ),
269 }
270}
271
272fn range_subtype_error(text: &str, type_name: &str) -> SQLError {
273 SQLError::Routine {
274 sqlstate: "22P02".into(),
275 message: format!("invalid input syntax for type {type_name}: \"{text}\""),
276 }
277}
278
279fn range_overflow(type_name: &str) -> SQLError {
280 SQLError::Routine {
281 sqlstate: "22003".into(),
282 message: format!("{type_name} out of range"),
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn discrete_ranges_canonicalize_to_inclusive_exclusive_bounds() {
292 assert_eq!(
293 parse_range("(1,4]", RangeSubtype::Integer)
294 .unwrap()
295 .to_text(),
296 "[2,5)"
297 );
298 assert_eq!(
299 parse_range("[2024-01-01,2024-01-02]", RangeSubtype::Date)
300 .unwrap()
301 .to_text(),
302 "[2024-01-01,2024-01-03)"
303 );
304 }
305
306 #[test]
307 fn multiranges_merge_overlapping_and_adjacent_members() {
308 assert_eq!(
309 parse_multirange("{[10,12),[1,3),[3,5)}", RangeSubtype::Integer)
310 .unwrap()
311 .to_text(),
312 "{[1,5),[10,12)}"
313 );
314 }
315
316 #[test]
317 fn range_relationships_cover_temporal_constraint_checks() {
318 let left = parse_range("[1,3)", RangeSubtype::Integer).unwrap();
319 let right = parse_range("[3,5)", RangeSubtype::Integer).unwrap();
320 let coverage = multirange_from_ranges(RangeSubtype::Integer, [left, right]);
321 let child = parse_range("[2,4)", RangeSubtype::Integer).unwrap();
322 assert!(coverage.contains_range(&child));
323 }
324}