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