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