1use super::{column_bounds::BoundsInner, committable_column::CommittableColumn, ColumnBounds};
2use crate::base::database::ColumnType;
3use core::fmt::Debug;
4use serde::{Deserialize, Serialize};
5use snafu::Snafu;
6
7#[derive(Debug, Snafu)]
9pub enum InvalidColumnCommitmentMetadata {
10 #[snafu(display("column of type {column_type} cannot have bounds like {column_bounds:?}"))]
12 TypeBoundsMismatch {
13 column_type: ColumnType,
14 column_bounds: ColumnBounds,
15 },
16}
17
18#[derive(Debug, Snafu)]
20#[snafu(display(
21 "column with type {datatype_a} cannot operate with column with type {datatype_b}"
22))]
23pub struct ColumnCommitmentMetadataMismatch {
24 datatype_a: ColumnType,
25 datatype_b: ColumnType,
26}
27
28const EXPECT_BOUNDS_MATCH_MESSAGE: &str = "we've already checked the column types match, which is a stronger requirement (mapping of type variants to bounds variants is surjective)";
29
30#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
32pub struct ColumnCommitmentMetadata {
33 column_type: ColumnType,
34 bounds: ColumnBounds,
35}
36
37impl ColumnCommitmentMetadata {
38 pub fn try_new(
43 column_type: ColumnType,
44 bounds: ColumnBounds,
45 ) -> Result<ColumnCommitmentMetadata, InvalidColumnCommitmentMetadata> {
46 match (column_type, bounds) {
47 (ColumnType::Uint8, ColumnBounds::Uint8(_))
48 | (ColumnType::TinyInt, ColumnBounds::TinyInt(_))
49 | (ColumnType::SmallInt, ColumnBounds::SmallInt(_))
50 | (ColumnType::Int, ColumnBounds::Int(_))
51 | (ColumnType::BigInt, ColumnBounds::BigInt(_))
52 | (ColumnType::Int128, ColumnBounds::Int128(_))
53 | (ColumnType::TimestampTZ(_, _), ColumnBounds::TimestampTZ(_))
54 | (
55 ColumnType::Boolean
56 | ColumnType::VarChar
57 | ColumnType::VarBinary
58 | ColumnType::Scalar
59 | ColumnType::Decimal75(..),
60 ColumnBounds::NoOrder,
61 ) => Ok(ColumnCommitmentMetadata {
62 column_type,
63 bounds,
64 }),
65 _ => Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch {
66 column_type,
67 column_bounds: bounds,
68 }),
69 }
70 }
71
72 #[expect(clippy::missing_panics_doc)]
73 #[must_use]
75 pub fn from_column_type_with_max_bounds(column_type: ColumnType) -> Self {
76 let bounds = match column_type {
77 ColumnType::SmallInt => ColumnBounds::SmallInt(super::Bounds::Bounded(
78 BoundsInner::try_new(i16::MIN, i16::MAX)
79 .expect("i16::MIN and i16::MAX are valid bounds for SmallInt"),
80 )),
81 ColumnType::Int => ColumnBounds::Int(super::Bounds::Bounded(
82 BoundsInner::try_new(i32::MIN, i32::MAX)
83 .expect("i32::MIN and i32::MAX are valid bounds for Int"),
84 )),
85 ColumnType::BigInt => ColumnBounds::BigInt(super::Bounds::Bounded(
86 BoundsInner::try_new(i64::MIN, i64::MAX)
87 .expect("i64::MIN and i64::MAX are valid bounds for BigInt"),
88 )),
89 ColumnType::TimestampTZ(_, _) => ColumnBounds::TimestampTZ(super::Bounds::Bounded(
90 BoundsInner::try_new(i64::MIN, i64::MAX)
91 .expect("i64::MIN and i64::MAX are valid bounds for TimeStamp"),
92 )),
93 ColumnType::Int128 => ColumnBounds::Int128(super::Bounds::Bounded(
94 BoundsInner::try_new(i128::MIN, i128::MAX)
95 .expect("i128::MIN and i128::MAX are valid bounds for Int128"),
96 )),
97 _ => ColumnBounds::NoOrder,
98 };
99 Self::try_new(column_type, bounds).expect("default bounds for column type are valid")
100 }
101
102 #[cfg(test)]
103 pub(super) fn bounds_mut(&mut self) -> &mut ColumnBounds {
104 &mut self.bounds
105 }
106
107 #[must_use]
109 pub fn column_type(&self) -> &ColumnType {
110 &self.column_type
111 }
112
113 #[must_use]
115 pub fn bounds(&self) -> &ColumnBounds {
116 &self.bounds
117 }
118
119 #[must_use]
121 pub fn from_column(column: &CommittableColumn) -> ColumnCommitmentMetadata {
122 ColumnCommitmentMetadata {
123 column_type: column.column_type(),
124 bounds: ColumnBounds::from_column(column),
125 }
126 }
127
128 #[expect(clippy::missing_panics_doc)]
132 pub fn try_union(
133 self,
134 other: ColumnCommitmentMetadata,
135 ) -> Result<ColumnCommitmentMetadata, ColumnCommitmentMetadataMismatch> {
136 if self.column_type != other.column_type {
137 return Err(ColumnCommitmentMetadataMismatch {
138 datatype_a: self.column_type,
139 datatype_b: other.column_type,
140 });
141 }
142
143 let bounds = self
144 .bounds
145 .try_union(other.bounds)
146 .expect(EXPECT_BOUNDS_MATCH_MESSAGE);
147
148 Ok(ColumnCommitmentMetadata {
149 bounds,
150 column_type: self.column_type,
151 })
152 }
153
154 #[expect(clippy::missing_panics_doc)]
159 pub fn try_difference(
160 self,
161 other: ColumnCommitmentMetadata,
162 ) -> Result<ColumnCommitmentMetadata, ColumnCommitmentMetadataMismatch> {
163 if self.column_type != other.column_type {
164 return Err(ColumnCommitmentMetadataMismatch {
165 datatype_a: self.column_type,
166 datatype_b: other.column_type,
167 });
168 }
169
170 let bounds = self
171 .bounds
172 .try_difference(other.bounds)
173 .expect(EXPECT_BOUNDS_MATCH_MESSAGE);
174
175 Ok(ColumnCommitmentMetadata {
176 bounds,
177 column_type: self.column_type,
178 })
179 }
180}
181
182#[cfg(test)]
183mod tests {
184
185 use super::*;
186 use crate::base::{
187 commitment::column_bounds::Bounds,
188 database::OwnedColumn,
189 math::decimal::Precision,
190 posql_time::{PoSQLTimeUnit, PoSQLTimeZone},
191 scalar::test_scalar::TestScalar,
192 };
193 use alloc::string::String;
194
195 #[test]
196 fn we_can_construct_metadata() {
197 assert_eq!(
198 ColumnCommitmentMetadata::try_new(
199 ColumnType::TinyInt,
200 ColumnBounds::TinyInt(Bounds::Empty)
201 )
202 .unwrap(),
203 ColumnCommitmentMetadata {
204 column_type: ColumnType::TinyInt,
205 bounds: ColumnBounds::TinyInt(Bounds::Empty)
206 }
207 );
208
209 assert_eq!(
210 ColumnCommitmentMetadata::try_new(
211 ColumnType::SmallInt,
212 ColumnBounds::SmallInt(Bounds::Empty)
213 )
214 .unwrap(),
215 ColumnCommitmentMetadata {
216 column_type: ColumnType::SmallInt,
217 bounds: ColumnBounds::SmallInt(Bounds::Empty)
218 }
219 );
220
221 assert_eq!(
222 ColumnCommitmentMetadata::try_new(ColumnType::Int, ColumnBounds::Int(Bounds::Empty))
223 .unwrap(),
224 ColumnCommitmentMetadata {
225 column_type: ColumnType::Int,
226 bounds: ColumnBounds::Int(Bounds::Empty)
227 }
228 );
229
230 assert_eq!(
231 ColumnCommitmentMetadata::try_new(
232 ColumnType::BigInt,
233 ColumnBounds::BigInt(Bounds::Empty)
234 )
235 .unwrap(),
236 ColumnCommitmentMetadata {
237 column_type: ColumnType::BigInt,
238 bounds: ColumnBounds::BigInt(Bounds::Empty)
239 }
240 );
241
242 assert_eq!(
243 ColumnCommitmentMetadata::try_new(ColumnType::Boolean, ColumnBounds::NoOrder,).unwrap(),
244 ColumnCommitmentMetadata {
245 column_type: ColumnType::Boolean,
246 bounds: ColumnBounds::NoOrder,
247 }
248 );
249
250 assert_eq!(
251 ColumnCommitmentMetadata::try_new(
252 ColumnType::Decimal75(Precision::new(10).unwrap(), 0),
253 ColumnBounds::NoOrder,
254 )
255 .unwrap(),
256 ColumnCommitmentMetadata {
257 column_type: ColumnType::Decimal75(Precision::new(10).unwrap(), 0),
258 bounds: ColumnBounds::NoOrder,
259 }
260 );
261
262 assert_eq!(
263 ColumnCommitmentMetadata::try_new(
264 ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc()),
265 ColumnBounds::TimestampTZ(Bounds::Empty),
266 )
267 .unwrap(),
268 ColumnCommitmentMetadata {
269 column_type: ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc()),
270 bounds: ColumnBounds::TimestampTZ(Bounds::Empty),
271 }
272 );
273
274 assert_eq!(
275 ColumnCommitmentMetadata::try_new(
276 ColumnType::Int128,
277 ColumnBounds::Int128(Bounds::sharp(-5, 10).unwrap())
278 )
279 .unwrap(),
280 ColumnCommitmentMetadata {
281 column_type: ColumnType::Int128,
282 bounds: ColumnBounds::Int128(Bounds::sharp(-5, 10).unwrap())
283 }
284 );
285
286 assert_eq!(
287 ColumnCommitmentMetadata::try_new(ColumnType::VarChar, ColumnBounds::NoOrder).unwrap(),
288 ColumnCommitmentMetadata {
289 column_type: ColumnType::VarChar,
290 bounds: ColumnBounds::NoOrder
291 }
292 );
293 }
294
295 #[test]
296 fn we_cannot_construct_metadata_with_type_bounds_mismatch() {
297 assert!(matches!(
298 ColumnCommitmentMetadata::try_new(
299 ColumnType::Boolean,
300 ColumnBounds::BigInt(Bounds::Empty)
301 ),
302 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
303 ));
304 assert!(matches!(
305 ColumnCommitmentMetadata::try_new(
306 ColumnType::Boolean,
307 ColumnBounds::Int128(Bounds::Empty)
308 ),
309 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
310 ));
311
312 assert!(matches!(
313 ColumnCommitmentMetadata::try_new(
314 ColumnType::Decimal75(Precision::new(10).unwrap(), 10),
315 ColumnBounds::Int128(Bounds::Empty)
316 ),
317 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
318 ));
319 assert!(matches!(
320 ColumnCommitmentMetadata::try_new(
321 ColumnType::Decimal75(Precision::new(10).unwrap(), 10),
322 ColumnBounds::BigInt(Bounds::Empty)
323 ),
324 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
325 ));
326
327 assert!(matches!(
328 ColumnCommitmentMetadata::try_new(
329 ColumnType::Scalar,
330 ColumnBounds::BigInt(Bounds::Empty)
331 ),
332 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
333 ));
334 assert!(matches!(
335 ColumnCommitmentMetadata::try_new(
336 ColumnType::Scalar,
337 ColumnBounds::Int128(Bounds::Empty)
338 ),
339 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
340 ));
341
342 assert!(matches!(
343 ColumnCommitmentMetadata::try_new(
344 ColumnType::BigInt,
345 ColumnBounds::Int128(Bounds::Empty)
346 ),
347 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
348 ));
349 assert!(matches!(
350 ColumnCommitmentMetadata::try_new(ColumnType::BigInt, ColumnBounds::NoOrder),
351 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
352 ));
353
354 assert!(matches!(
355 ColumnCommitmentMetadata::try_new(
356 ColumnType::Int128,
357 ColumnBounds::BigInt(Bounds::Empty)
358 ),
359 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
360 ));
361 assert!(matches!(
362 ColumnCommitmentMetadata::try_new(ColumnType::Int128, ColumnBounds::NoOrder),
363 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
364 ));
365
366 assert!(matches!(
367 ColumnCommitmentMetadata::try_new(
368 ColumnType::VarChar,
369 ColumnBounds::BigInt(Bounds::Empty)
370 ),
371 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
372 ));
373 assert!(matches!(
374 ColumnCommitmentMetadata::try_new(
375 ColumnType::VarChar,
376 ColumnBounds::Int128(Bounds::Empty)
377 ),
378 Err(InvalidColumnCommitmentMetadata::TypeBoundsMismatch { .. })
379 ));
380 }
381
382 #[test]
383 fn we_can_construct_metadata_from_column() {
384 let boolean_column =
385 OwnedColumn::<TestScalar>::Boolean([true, false, true, false, true].to_vec());
386 let committable_boolean_column = CommittableColumn::from(&boolean_column);
387 let boolean_metadata = ColumnCommitmentMetadata::from_column(&committable_boolean_column);
388 assert_eq!(boolean_metadata.column_type(), &ColumnType::Boolean);
389 assert_eq!(boolean_metadata.bounds(), &ColumnBounds::NoOrder);
390
391 let decimal_column = OwnedColumn::<TestScalar>::Decimal75(
392 Precision::new(10).unwrap(),
393 0,
394 [1, 2, 3, 4, 5].map(TestScalar::from).to_vec(),
395 );
396 let committable_decimal_column = CommittableColumn::from(&decimal_column);
397 let decimal_metadata = ColumnCommitmentMetadata::from_column(&committable_decimal_column);
398 assert_eq!(
399 decimal_metadata.column_type(),
400 &ColumnType::Decimal75(Precision::new(10).unwrap(), 0)
401 );
402 assert_eq!(decimal_metadata.bounds(), &ColumnBounds::NoOrder);
403
404 let timestamp_column: OwnedColumn<TestScalar> = OwnedColumn::<TestScalar>::TimestampTZ(
405 PoSQLTimeUnit::Second,
406 PoSQLTimeZone::utc(),
407 [1i64, 2, 3, 4, 5].to_vec(),
408 );
409 let committable_timestamp_column = CommittableColumn::from(×tamp_column);
410 let timestamp_metadata =
411 ColumnCommitmentMetadata::from_column(&committable_timestamp_column);
412 assert_eq!(
413 timestamp_metadata.column_type(),
414 &ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc())
415 );
416 if let ColumnBounds::TimestampTZ(Bounds::Sharp(bounds)) = timestamp_metadata.bounds() {
417 assert_eq!(bounds.min(), &1);
418 assert_eq!(bounds.max(), &5);
419 } else {
420 panic!("Bounds constructed from nonempty TimestampTZ column should be ColumnBounds::TimestampTZ(Bounds::Sharp(_))");
421 }
422
423 let varchar_column = OwnedColumn::<TestScalar>::VarChar(
424 ["Lorem", "ipsum", "dolor", "sit", "amet"]
425 .map(String::from)
426 .to_vec(),
427 );
428 let committable_varchar_column = CommittableColumn::from(&varchar_column);
429 let varchar_metadata = ColumnCommitmentMetadata::from_column(&committable_varchar_column);
430 assert_eq!(varchar_metadata.column_type(), &ColumnType::VarChar);
431 assert_eq!(varchar_metadata.bounds(), &ColumnBounds::NoOrder);
432
433 let bigint_column = OwnedColumn::<TestScalar>::BigInt([1, 2, 3, 1, 0].to_vec());
434 let committable_bigint_column = CommittableColumn::from(&bigint_column);
435 let bigint_metadata = ColumnCommitmentMetadata::from_column(&committable_bigint_column);
436 assert_eq!(bigint_metadata.column_type(), &ColumnType::BigInt);
437 if let ColumnBounds::BigInt(Bounds::Sharp(bounds)) = bigint_metadata.bounds() {
438 assert_eq!(bounds.min(), &0);
439 assert_eq!(bounds.max(), &3);
440 } else {
441 panic!("Bounds constructed from nonempty BigInt column should be ColumnBounds::BigInt(Bounds::Sharp(_))");
442 }
443
444 let int_column = OwnedColumn::<TestScalar>::Int([1, 2, 3, 1, 0].to_vec());
445 let committable_int_column = CommittableColumn::from(&int_column);
446 let int_metadata = ColumnCommitmentMetadata::from_column(&committable_int_column);
447 assert_eq!(int_metadata.column_type(), &ColumnType::Int);
448 if let ColumnBounds::Int(Bounds::Sharp(bounds)) = int_metadata.bounds() {
449 assert_eq!(bounds.min(), &0);
450 assert_eq!(bounds.max(), &3);
451 } else {
452 panic!("Bounds constructed from nonempty Int column should be ColumnBounds::Int(Bounds::Sharp(_))");
453 }
454
455 let tinyint_column = OwnedColumn::<TestScalar>::TinyInt([1, 2, 3, 1, 0].to_vec());
456 let committable_tinyint_column = CommittableColumn::from(&tinyint_column);
457 let tinyint_metadata = ColumnCommitmentMetadata::from_column(&committable_tinyint_column);
458 assert_eq!(tinyint_metadata.column_type(), &ColumnType::TinyInt);
459 if let ColumnBounds::TinyInt(Bounds::Sharp(bounds)) = tinyint_metadata.bounds() {
460 assert_eq!(bounds.min(), &0);
461 assert_eq!(bounds.max(), &3);
462 } else {
463 panic!("Bounds constructed from nonempty TinyInt column should be ColumnBounds::TinyInt(Bounds::Sharp(_))");
464 }
465
466 let smallint_column = OwnedColumn::<TestScalar>::SmallInt([1, 2, 3, 1, 0].to_vec());
467 let committable_smallint_column = CommittableColumn::from(&smallint_column);
468 let smallint_metadata = ColumnCommitmentMetadata::from_column(&committable_smallint_column);
469 assert_eq!(smallint_metadata.column_type(), &ColumnType::SmallInt);
470 if let ColumnBounds::SmallInt(Bounds::Sharp(bounds)) = smallint_metadata.bounds() {
471 assert_eq!(bounds.min(), &0);
472 assert_eq!(bounds.max(), &3);
473 } else {
474 panic!("Bounds constructed from nonempty SmallInt column should be ColumnBounds::SmallInt(Bounds::Sharp(_))");
475 }
476
477 let int128_column = OwnedColumn::<TestScalar>::Int128([].to_vec());
478 let committable_int128_column = CommittableColumn::from(&int128_column);
479 let int128_metadata = ColumnCommitmentMetadata::from_column(&committable_int128_column);
480 assert_eq!(int128_metadata.column_type(), &ColumnType::Int128);
481 assert_eq!(
482 int128_metadata.bounds(),
483 &ColumnBounds::Int128(Bounds::Empty)
484 );
485
486 let scalar_column = OwnedColumn::Scalar([1, 2, 3, 4, 5].map(TestScalar::from).to_vec());
487 let committable_scalar_column = CommittableColumn::from(&scalar_column);
488 let scalar_metadata = ColumnCommitmentMetadata::from_column(&committable_scalar_column);
489 assert_eq!(scalar_metadata.column_type(), &ColumnType::Scalar);
490 assert_eq!(scalar_metadata.bounds(), &ColumnBounds::NoOrder);
491 }
492
493 #[test]
494 fn we_can_union_matching_metadata() {
495 let boolean_metadata = ColumnCommitmentMetadata {
497 column_type: ColumnType::Boolean,
498 bounds: ColumnBounds::NoOrder,
499 };
500 assert_eq!(
501 boolean_metadata.try_union(boolean_metadata).unwrap(),
502 boolean_metadata
503 );
504
505 let decimal_metadata = ColumnCommitmentMetadata {
506 column_type: ColumnType::Decimal75(Precision::new(12).unwrap(), 0),
507 bounds: ColumnBounds::NoOrder,
508 };
509 assert_eq!(
510 decimal_metadata.try_union(decimal_metadata).unwrap(),
511 decimal_metadata
512 );
513
514 let varchar_metadata = ColumnCommitmentMetadata {
515 column_type: ColumnType::VarChar,
516 bounds: ColumnBounds::NoOrder,
517 };
518 assert_eq!(
519 varchar_metadata.try_union(varchar_metadata).unwrap(),
520 varchar_metadata
521 );
522
523 let scalar_metadata = ColumnCommitmentMetadata {
524 column_type: ColumnType::Scalar,
525 bounds: ColumnBounds::NoOrder,
526 };
527 assert_eq!(
528 scalar_metadata.try_union(scalar_metadata).unwrap(),
529 scalar_metadata
530 );
531
532 let ints = [1, 2, 3, 1, 0];
534 let tinyint_column_a = CommittableColumn::TinyInt(&ints[..2]);
535 let tinyint_metadata_a = ColumnCommitmentMetadata::from_column(&tinyint_column_a);
536 let tinyint_column_b = CommittableColumn::TinyInt(&ints[2..]);
537 let tinyint_metadata_b = ColumnCommitmentMetadata::from_column(&tinyint_column_b);
538 let tinyint_column_c = CommittableColumn::TinyInt(&ints);
539 let tinyint_metadata_c = ColumnCommitmentMetadata::from_column(&tinyint_column_c);
540 assert_eq!(
541 tinyint_metadata_a.try_union(tinyint_metadata_b).unwrap(),
542 tinyint_metadata_c
543 );
544
545 let ints = [1, 2, 3, 1, 0];
546 let smallint_column_a = CommittableColumn::SmallInt(&ints[..2]);
547 let smallint_metadata_a = ColumnCommitmentMetadata::from_column(&smallint_column_a);
548 let smallint_column_b = CommittableColumn::SmallInt(&ints[2..]);
549 let smallint_metadata_b = ColumnCommitmentMetadata::from_column(&smallint_column_b);
550 let smallint_column_c = CommittableColumn::SmallInt(&ints);
551 let smallint_metadata_c = ColumnCommitmentMetadata::from_column(&smallint_column_c);
552 assert_eq!(
553 smallint_metadata_a.try_union(smallint_metadata_b).unwrap(),
554 smallint_metadata_c
555 );
556
557 let ints = [1, 2, 3, 1, 0];
558 let int_column_a = CommittableColumn::Int(&ints[..2]);
559 let int_metadata_a = ColumnCommitmentMetadata::from_column(&int_column_a);
560 let int_column_b = CommittableColumn::Int(&ints[2..]);
561 let int_metadata_b = ColumnCommitmentMetadata::from_column(&int_column_b);
562 let int_column_c = CommittableColumn::Int(&ints);
563 let int_metadata_c = ColumnCommitmentMetadata::from_column(&int_column_c);
564 assert_eq!(
565 int_metadata_a.try_union(int_metadata_b).unwrap(),
566 int_metadata_c
567 );
568
569 let ints = [1, 2, 3, 1, 0];
570 let bigint_column_a = CommittableColumn::BigInt(&ints[..2]);
571 let bigint_metadata_a = ColumnCommitmentMetadata::from_column(&bigint_column_a);
572 let bigint_column_b = CommittableColumn::BigInt(&ints[2..]);
573 let bigint_metadata_b = ColumnCommitmentMetadata::from_column(&bigint_column_b);
574 let bigint_column_c = CommittableColumn::BigInt(&ints);
575 let bigint_metadata_c = ColumnCommitmentMetadata::from_column(&bigint_column_c);
576 assert_eq!(
577 bigint_metadata_a.try_union(bigint_metadata_b).unwrap(),
578 bigint_metadata_c
579 );
580
581 let times = [
584 1_625_072_400,
585 1_625_076_000,
586 1_625_079_600,
587 1_625_072_400,
588 1_625_065_000,
589 ];
590 let timezone = PoSQLTimeZone::utc();
591 let timeunit = PoSQLTimeUnit::Second;
592 let timestamp_column_a = CommittableColumn::TimestampTZ(timeunit, timezone, ×[..2]);
593 let timestamp_metadata_a = ColumnCommitmentMetadata::from_column(×tamp_column_a);
594 let timestamp_column_b = CommittableColumn::TimestampTZ(timeunit, timezone, ×[2..]);
595 let timestamp_metadata_b = ColumnCommitmentMetadata::from_column(×tamp_column_b);
596 let timestamp_column_c = CommittableColumn::TimestampTZ(timeunit, timezone, ×);
597 let timestamp_metadata_c = ColumnCommitmentMetadata::from_column(×tamp_column_c);
598 assert_eq!(
599 timestamp_metadata_a
600 .try_union(timestamp_metadata_b)
601 .unwrap(),
602 timestamp_metadata_c
603 );
604 }
605
606 #[test]
607 fn we_can_difference_timestamp_tz_matching_metadata() {
608 let times = [
610 1_625_072_400,
611 1_625_076_000,
612 1_625_079_600,
613 1_625_072_400,
614 1_625_065_000,
615 ];
616 let timezone = PoSQLTimeZone::utc();
617 let timeunit = PoSQLTimeUnit::Second;
618
619 let timestamp_column_a = CommittableColumn::TimestampTZ(timeunit, timezone, ×[..2]);
620 let timestamp_metadata_a = ColumnCommitmentMetadata::from_column(×tamp_column_a);
621 let timestamp_column_b = CommittableColumn::TimestampTZ(timeunit, timezone, ×);
622 let timestamp_metadata_b = ColumnCommitmentMetadata::from_column(×tamp_column_b);
623
624 let b_difference_a = timestamp_metadata_b
625 .try_difference(timestamp_metadata_a)
626 .unwrap();
627 assert_eq!(
628 b_difference_a.column_type,
629 ColumnType::TimestampTZ(timeunit, timezone)
630 );
631 if let ColumnBounds::TimestampTZ(Bounds::Bounded(bounds)) = b_difference_a.bounds {
632 assert_eq!(bounds.min(), &1_625_065_000);
633 assert_eq!(bounds.max(), &1_625_079_600);
634 } else {
635 panic!("difference of overlapping bounds should be Bounded");
636 }
637
638 let timestamp_column_empty = CommittableColumn::TimestampTZ(timeunit, timezone, &[]);
639 let timestamp_metadata_empty =
640 ColumnCommitmentMetadata::from_column(×tamp_column_empty);
641
642 assert_eq!(
643 timestamp_metadata_b
644 .try_difference(timestamp_metadata_empty)
645 .unwrap(),
646 timestamp_metadata_b
647 );
648 assert_eq!(
649 timestamp_metadata_empty
650 .try_difference(timestamp_metadata_b)
651 .unwrap(),
652 timestamp_metadata_empty
653 );
654 }
655
656 #[test]
657 fn we_can_difference_bigint_matching_metadata() {
658 let ints = [1, 2, 3, 1, 0];
660 let bigint_column_a = CommittableColumn::BigInt(&ints[..2]);
661 let bigint_metadata_a = ColumnCommitmentMetadata::from_column(&bigint_column_a);
662 let bigint_column_b = CommittableColumn::BigInt(&ints);
663 let bigint_metadata_b = ColumnCommitmentMetadata::from_column(&bigint_column_b);
664
665 let b_difference_a = bigint_metadata_b.try_difference(bigint_metadata_a).unwrap();
666 assert_eq!(b_difference_a.column_type, ColumnType::BigInt);
667 if let ColumnBounds::BigInt(Bounds::Bounded(bounds)) = b_difference_a.bounds() {
668 assert_eq!(bounds.min(), &0);
669 assert_eq!(bounds.max(), &3);
670 } else {
671 panic!("difference of overlapping bounds should be Bounded");
672 }
673
674 let bigint_column_empty = CommittableColumn::BigInt(&[]);
675 let bigint_metadata_empty = ColumnCommitmentMetadata::from_column(&bigint_column_empty);
676
677 assert_eq!(
678 bigint_metadata_b
679 .try_difference(bigint_metadata_empty)
680 .unwrap(),
681 bigint_metadata_b
682 );
683 assert_eq!(
684 bigint_metadata_empty
685 .try_difference(bigint_metadata_b)
686 .unwrap(),
687 bigint_metadata_empty
688 );
689 }
690
691 #[test]
692 fn we_can_difference_tinyint_matching_metadata() {
693 let tinyints = [1, 2, 3, 1, 0];
695 let tinyint_column_a = CommittableColumn::TinyInt(&tinyints[..2]);
696 let tinyint_metadata_a = ColumnCommitmentMetadata::from_column(&tinyint_column_a);
697 let tinyint_column_b = CommittableColumn::TinyInt(&tinyints);
698 let tinyint_metadata_b = ColumnCommitmentMetadata::from_column(&tinyint_column_b);
699
700 let b_difference_a = tinyint_metadata_b
701 .try_difference(tinyint_metadata_a)
702 .unwrap();
703 assert_eq!(b_difference_a.column_type, ColumnType::TinyInt);
704 if let ColumnBounds::TinyInt(Bounds::Bounded(bounds)) = b_difference_a.bounds() {
705 assert_eq!(bounds.min(), &0);
706 assert_eq!(bounds.max(), &3);
707 } else {
708 panic!("difference of overlapping bounds should be Bounded");
709 }
710
711 let tinyint_column_empty = CommittableColumn::TinyInt(&[]);
712 let tinyint_metadata_empty = ColumnCommitmentMetadata::from_column(&tinyint_column_empty);
713
714 assert_eq!(
715 tinyint_metadata_b
716 .try_difference(tinyint_metadata_empty)
717 .unwrap(),
718 tinyint_metadata_b
719 );
720 assert_eq!(
721 tinyint_metadata_empty
722 .try_difference(tinyint_metadata_b)
723 .unwrap(),
724 tinyint_metadata_empty
725 );
726 }
727
728 #[test]
729 fn we_can_difference_smallint_matching_metadata() {
730 let smallints = [1, 2, 3, 1, 0];
732 let smallint_column_a = CommittableColumn::SmallInt(&smallints[..2]);
733 let smallint_metadata_a = ColumnCommitmentMetadata::from_column(&smallint_column_a);
734 let smallint_column_b = CommittableColumn::SmallInt(&smallints);
735 let smallint_metadata_b = ColumnCommitmentMetadata::from_column(&smallint_column_b);
736
737 let b_difference_a = smallint_metadata_b
738 .try_difference(smallint_metadata_a)
739 .unwrap();
740 assert_eq!(b_difference_a.column_type, ColumnType::SmallInt);
741 if let ColumnBounds::SmallInt(Bounds::Bounded(bounds)) = b_difference_a.bounds() {
742 assert_eq!(bounds.min(), &0);
743 assert_eq!(bounds.max(), &3);
744 } else {
745 panic!("difference of overlapping bounds should be Bounded");
746 }
747
748 let smallint_column_empty = CommittableColumn::SmallInt(&[]);
749 let smallint_metadata_empty = ColumnCommitmentMetadata::from_column(&smallint_column_empty);
750
751 assert_eq!(
752 smallint_metadata_b
753 .try_difference(smallint_metadata_empty)
754 .unwrap(),
755 smallint_metadata_b
756 );
757 assert_eq!(
758 smallint_metadata_empty
759 .try_difference(smallint_metadata_b)
760 .unwrap(),
761 smallint_metadata_empty
762 );
763 }
764
765 #[test]
766 fn we_can_difference_int_matching_metadata() {
767 let ints = [1, 2, 3, 1, 0];
769 let int_column_a = CommittableColumn::Int(&ints[..2]);
770 let int_metadata_a = ColumnCommitmentMetadata::from_column(&int_column_a);
771 let int_column_b = CommittableColumn::Int(&ints);
772 let int_metadata_b = ColumnCommitmentMetadata::from_column(&int_column_b);
773
774 let b_difference_a = int_metadata_b.try_difference(int_metadata_a).unwrap();
775 assert_eq!(b_difference_a.column_type, ColumnType::Int);
776 if let ColumnBounds::Int(Bounds::Bounded(bounds)) = b_difference_a.bounds() {
777 assert_eq!(bounds.min(), &0);
778 assert_eq!(bounds.max(), &3);
779 } else {
780 panic!("difference of overlapping bounds should be Bounded");
781 }
782
783 let int_column_empty = CommittableColumn::Int(&[]);
784 let int_metadata_empty = ColumnCommitmentMetadata::from_column(&int_column_empty);
785
786 assert_eq!(
787 int_metadata_b.try_difference(int_metadata_empty).unwrap(),
788 int_metadata_b
789 );
790 assert_eq!(
791 int_metadata_empty.try_difference(int_metadata_b).unwrap(),
792 int_metadata_empty
793 );
794 }
795
796 #[expect(clippy::too_many_lines)]
797 #[test]
798 fn we_cannot_perform_arithmetic_on_mismatched_metadata() {
799 let boolean_metadata = ColumnCommitmentMetadata {
800 column_type: ColumnType::Boolean,
801 bounds: ColumnBounds::NoOrder,
802 };
803 let varchar_metadata = ColumnCommitmentMetadata {
804 column_type: ColumnType::VarChar,
805 bounds: ColumnBounds::NoOrder,
806 };
807 let scalar_metadata = ColumnCommitmentMetadata {
808 column_type: ColumnType::Scalar,
809 bounds: ColumnBounds::NoOrder,
810 };
811 let tinyint_metadata = ColumnCommitmentMetadata {
812 column_type: ColumnType::TinyInt,
813 bounds: ColumnBounds::TinyInt(Bounds::Empty),
814 };
815 let smallint_metadata = ColumnCommitmentMetadata {
816 column_type: ColumnType::SmallInt,
817 bounds: ColumnBounds::SmallInt(Bounds::Empty),
818 };
819 let int_metadata = ColumnCommitmentMetadata {
820 column_type: ColumnType::Int,
821 bounds: ColumnBounds::Int(Bounds::Empty),
822 };
823 let bigint_metadata = ColumnCommitmentMetadata {
824 column_type: ColumnType::BigInt,
825 bounds: ColumnBounds::BigInt(Bounds::Empty),
826 };
827 let int128_metadata = ColumnCommitmentMetadata {
828 column_type: ColumnType::Int128,
829 bounds: ColumnBounds::Int128(Bounds::Empty),
830 };
831 let decimal75_metadata = ColumnCommitmentMetadata {
832 column_type: ColumnType::Decimal75(Precision::new(4).unwrap(), 8),
833 bounds: ColumnBounds::Int128(Bounds::Empty),
834 };
835
836 assert!(tinyint_metadata.try_union(scalar_metadata).is_err());
837 assert!(scalar_metadata.try_union(tinyint_metadata).is_err());
838
839 assert!(tinyint_metadata.try_union(decimal75_metadata).is_err());
840 assert!(decimal75_metadata.try_union(tinyint_metadata).is_err());
841
842 assert!(tinyint_metadata.try_union(varchar_metadata).is_err());
843 assert!(varchar_metadata.try_union(tinyint_metadata).is_err());
844
845 assert!(tinyint_metadata.try_union(boolean_metadata).is_err());
846 assert!(boolean_metadata.try_union(tinyint_metadata).is_err());
847
848 assert!(smallint_metadata.try_union(scalar_metadata).is_err());
849 assert!(scalar_metadata.try_union(smallint_metadata).is_err());
850
851 assert!(smallint_metadata.try_union(decimal75_metadata).is_err());
852 assert!(decimal75_metadata.try_union(smallint_metadata).is_err());
853
854 assert!(smallint_metadata.try_union(varchar_metadata).is_err());
855 assert!(varchar_metadata.try_union(smallint_metadata).is_err());
856
857 assert!(smallint_metadata.try_union(boolean_metadata).is_err());
858 assert!(boolean_metadata.try_union(smallint_metadata).is_err());
859
860 assert!(int_metadata.try_union(scalar_metadata).is_err());
861 assert!(scalar_metadata.try_union(int_metadata).is_err());
862
863 assert!(int_metadata.try_union(decimal75_metadata).is_err());
864 assert!(decimal75_metadata.try_union(int_metadata).is_err());
865
866 assert!(int_metadata.try_union(varchar_metadata).is_err());
867 assert!(varchar_metadata.try_union(int_metadata).is_err());
868
869 assert!(int_metadata.try_union(boolean_metadata).is_err());
870 assert!(boolean_metadata.try_union(int_metadata).is_err());
871
872 assert!(varchar_metadata.try_union(scalar_metadata).is_err());
873 assert!(scalar_metadata.try_union(varchar_metadata).is_err());
874
875 assert!(varchar_metadata.try_union(bigint_metadata).is_err());
876 assert!(bigint_metadata.try_union(varchar_metadata).is_err());
877
878 assert!(varchar_metadata.try_union(int128_metadata).is_err());
879 assert!(int128_metadata.try_union(varchar_metadata).is_err());
880
881 assert!(decimal75_metadata.try_union(scalar_metadata).is_err());
882 assert!(scalar_metadata.try_union(decimal75_metadata).is_err());
883
884 assert!(decimal75_metadata.try_union(bigint_metadata).is_err());
885 assert!(bigint_metadata.try_union(decimal75_metadata).is_err());
886
887 assert!(decimal75_metadata.try_union(varchar_metadata).is_err());
888 assert!(varchar_metadata.try_union(decimal75_metadata).is_err());
889
890 assert!(decimal75_metadata.try_union(int128_metadata).is_err());
891 assert!(int128_metadata.try_union(decimal75_metadata).is_err());
892
893 assert!(scalar_metadata.try_union(bigint_metadata).is_err());
894 assert!(bigint_metadata.try_union(scalar_metadata).is_err());
895
896 assert!(scalar_metadata.try_union(int128_metadata).is_err());
897 assert!(int128_metadata.try_union(scalar_metadata).is_err());
898
899 assert!(bigint_metadata.try_union(int128_metadata).is_err());
900 assert!(int128_metadata.try_union(bigint_metadata).is_err());
901
902 assert!(varchar_metadata.try_difference(scalar_metadata).is_err());
903 assert!(scalar_metadata.try_difference(varchar_metadata).is_err());
904
905 assert!(varchar_metadata.try_difference(bigint_metadata).is_err());
906 assert!(bigint_metadata.try_difference(varchar_metadata).is_err());
907
908 assert!(varchar_metadata.try_difference(int128_metadata).is_err());
909 assert!(int128_metadata.try_difference(varchar_metadata).is_err());
910
911 assert!(scalar_metadata.try_difference(bigint_metadata).is_err());
912 assert!(bigint_metadata.try_difference(scalar_metadata).is_err());
913
914 assert!(scalar_metadata.try_difference(int128_metadata).is_err());
915 assert!(int128_metadata.try_difference(scalar_metadata).is_err());
916
917 assert!(bigint_metadata.try_difference(int128_metadata).is_err());
918 assert!(int128_metadata.try_difference(bigint_metadata).is_err());
919
920 assert!(decimal75_metadata.try_difference(scalar_metadata).is_err());
921 assert!(scalar_metadata.try_difference(decimal75_metadata).is_err());
922
923 assert!(decimal75_metadata.try_difference(bigint_metadata).is_err());
924 assert!(bigint_metadata.try_difference(decimal75_metadata).is_err());
925
926 assert!(decimal75_metadata.try_difference(int128_metadata).is_err());
927 assert!(int128_metadata.try_difference(decimal75_metadata).is_err());
928
929 assert!(decimal75_metadata.try_difference(varchar_metadata).is_err());
930 assert!(varchar_metadata.try_difference(decimal75_metadata).is_err());
931
932 assert!(decimal75_metadata.try_difference(boolean_metadata).is_err());
933 assert!(boolean_metadata.try_difference(decimal75_metadata).is_err());
934
935 assert!(boolean_metadata.try_difference(bigint_metadata).is_err());
936 assert!(bigint_metadata.try_difference(boolean_metadata).is_err());
937
938 assert!(boolean_metadata.try_difference(int128_metadata).is_err());
939 assert!(int128_metadata.try_difference(boolean_metadata).is_err());
940
941 assert!(boolean_metadata.try_difference(varchar_metadata).is_err());
942 assert!(varchar_metadata.try_difference(boolean_metadata).is_err());
943
944 assert!(boolean_metadata.try_difference(scalar_metadata).is_err());
945 assert!(scalar_metadata.try_difference(boolean_metadata).is_err());
946
947 let different_decimal75_metadata = ColumnCommitmentMetadata {
948 column_type: ColumnType::Decimal75(Precision::new(75).unwrap(), 0),
949 bounds: ColumnBounds::Int128(Bounds::Empty),
950 };
951
952 assert!(decimal75_metadata
953 .try_difference(different_decimal75_metadata)
954 .is_err());
955 assert!(different_decimal75_metadata
956 .try_difference(decimal75_metadata)
957 .is_err());
958
959 assert!(decimal75_metadata
960 .try_union(different_decimal75_metadata)
961 .is_err());
962 assert!(different_decimal75_metadata
963 .try_union(decimal75_metadata)
964 .is_err());
965
966 let timestamp_tz_metadata_a = ColumnCommitmentMetadata {
967 column_type: ColumnType::TimestampTZ(PoSQLTimeUnit::Second, PoSQLTimeZone::utc()),
968 bounds: ColumnBounds::TimestampTZ(Bounds::Empty),
969 };
970
971 let timestamp_tz_metadata_b = ColumnCommitmentMetadata {
972 column_type: ColumnType::TimestampTZ(PoSQLTimeUnit::Millisecond, PoSQLTimeZone::utc()),
973 bounds: ColumnBounds::TimestampTZ(Bounds::Empty),
974 };
975
976 assert!(timestamp_tz_metadata_a.try_union(varchar_metadata).is_err());
978 assert!(varchar_metadata.try_union(timestamp_tz_metadata_a).is_err());
979
980 assert!(timestamp_tz_metadata_a
982 .try_difference(scalar_metadata)
983 .is_err());
984 assert!(scalar_metadata
985 .try_difference(timestamp_tz_metadata_a)
986 .is_err());
987
988 assert!(timestamp_tz_metadata_a
990 .try_union(timestamp_tz_metadata_b)
991 .is_err());
992 assert!(timestamp_tz_metadata_b
993 .try_union(timestamp_tz_metadata_a)
994 .is_err());
995
996 assert!(timestamp_tz_metadata_a
998 .try_difference(timestamp_tz_metadata_b)
999 .is_err());
1000 assert!(timestamp_tz_metadata_b
1001 .try_difference(timestamp_tz_metadata_a)
1002 .is_err());
1003 }
1004}