1use std::collections::{BTreeMap, BTreeSet};
11#[cfg(any(test, feature = "proptest"))]
12use std::rc::Rc;
13use std::{fmt, vec};
14
15use anyhow::bail;
16use itertools::Itertools;
17use mz_ore::cast::CastFrom;
18use mz_ore::soft_panic_or_log;
19use mz_ore::str::StrExt;
20use mz_ore::{assert_none, assert_ok};
21use mz_persist_types::schema::SchemaId;
22use mz_proto::{IntoRustIfSome, ProtoType, RustType, TryFromProtoError};
23#[cfg(any(test, feature = "proptest"))]
24use proptest::prelude::*;
25#[cfg(any(test, feature = "proptest"))]
26use proptest::strategy::{Strategy, Union};
27#[cfg(any(test, feature = "proptest"))]
28use proptest_derive::Arbitrary;
29use serde::{Deserialize, Serialize};
30
31#[cfg(any(test, feature = "proptest"))]
32use crate::Row;
33#[cfg(any(test, feature = "proptest"))]
34use crate::arb_datum_for_column;
35use crate::relation_and_scalar::proto_relation_type::ProtoKey;
36pub use crate::relation_and_scalar::{
37 ProtoColumnMetadata, ProtoColumnName, ProtoColumnType, ProtoRelationDesc, ProtoRelationType,
38 ProtoRelationVersion,
39};
40use crate::{Datum, ReprScalarType, SqlScalarType};
41
42#[derive(
50 Clone,
51 Debug,
52 Eq,
53 PartialEq,
54 Ord,
55 PartialOrd,
56 Serialize,
57 Deserialize,
58 Hash
59)]
60#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
61pub struct SqlColumnType {
62 pub scalar_type: SqlScalarType,
64 #[serde(default = "return_true")]
66 pub nullable: bool,
67}
68
69#[inline(always)]
75fn return_true() -> bool {
76 true
77}
78
79impl SqlColumnType {
80 pub fn try_union_many<'a>(
84 typs: impl IntoIterator<Item = &'a Self>,
85 ) -> Result<Self, anyhow::Error> {
86 let mut iter = typs.into_iter();
87 let Some(typ) = iter.next() else {
88 bail!("Cannot union empty iterator");
89 };
90 iter.try_fold(typ.clone(), |a, b| a.try_union(b))
91 }
92
93 pub fn union_many<'a>(typs: impl IntoIterator<Item = &'a Self>) -> Self {
98 Self::try_union_many(typs).expect("Cannot union empty iterator")
99 }
100
101 pub fn backport_nullability(&mut self, backport_typ: &ReprColumnType) {
105 self.scalar_type
106 .backport_nullability(&backport_typ.scalar_type);
107 self.nullable = backport_typ.nullable;
108 }
109
110 pub fn sql_union(&self, other: &Self) -> Result<Self, anyhow::Error> {
119 Ok(SqlColumnType {
120 scalar_type: self.scalar_type.sql_union(&other.scalar_type)?,
121 nullable: self.nullable || other.nullable,
122 })
123 }
124
125 pub fn try_union(&self, other: &Self) -> Result<Self, anyhow::Error> {
135 self.sql_union(other).or_else(|e| {
136 let repr_self = ReprColumnType::from(self);
137 let repr_other = ReprColumnType::from(other);
138 match repr_self.union(&repr_other) {
139 Ok(typ) => {
140 soft_panic_or_log!("repr type error: sql_union({self:?}, {other:?}): {e}");
143 Ok(SqlColumnType::from_repr(&typ))
144 }
145 Err(_) => {
146 Err(e)
149 }
150 }
151 })
152 }
153
154 pub fn union(&self, other: &Self) -> Self {
159 self.try_union(other).unwrap_or_else(|e| {
160 panic!("repr type error: after sql_union({self:?}, {other:?}) error: {e}")
161 })
162 }
163
164 pub fn nullable(mut self, nullable: bool) -> Self {
167 self.nullable = nullable;
168 self
169 }
170}
171
172impl RustType<ProtoColumnType> for SqlColumnType {
173 fn into_proto(&self) -> ProtoColumnType {
174 ProtoColumnType {
175 nullable: self.nullable,
176 scalar_type: Some(self.scalar_type.into_proto()),
177 }
178 }
179
180 fn from_proto(proto: ProtoColumnType) -> Result<Self, TryFromProtoError> {
181 Ok(SqlColumnType {
182 nullable: proto.nullable,
183 scalar_type: proto
184 .scalar_type
185 .into_rust_if_some("ProtoColumnType::scalar_type")?,
186 })
187 }
188}
189
190impl fmt::Display for SqlColumnType {
191 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192 let nullable = if self.nullable { "Null" } else { "NotNull" };
193 f.write_fmt(format_args!("{:?}:{}", self.scalar_type, nullable))
194 }
195}
196
197#[derive(
199 Clone,
200 Debug,
201 Eq,
202 PartialEq,
203 Ord,
204 PartialOrd,
205 Serialize,
206 Deserialize,
207 Hash
208)]
209#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
210pub struct SqlRelationType {
211 pub column_types: Vec<SqlColumnType>,
213 #[serde(default)]
223 pub keys: Vec<Vec<usize>>,
224}
225
226impl SqlRelationType {
227 pub fn empty() -> Self {
230 SqlRelationType::new(vec![])
231 }
232
233 pub fn new(column_types: Vec<SqlColumnType>) -> Self {
237 SqlRelationType {
238 column_types,
239 keys: Vec::new(),
240 }
241 }
242
243 pub fn with_key(mut self, mut indices: Vec<usize>) -> Self {
245 indices.sort_unstable();
246 if !self.keys.contains(&indices) {
247 self.keys.push(indices);
248 }
249 self
250 }
251
252 pub fn with_keys(mut self, keys: Vec<Vec<usize>>) -> Self {
253 for key in keys {
254 self = self.with_key(key)
255 }
256 self
257 }
258
259 pub fn arity(&self) -> usize {
261 self.column_types.len()
262 }
263
264 pub fn default_key(&self) -> Vec<usize> {
266 if let Some(key) = self.keys.first() {
267 if key.is_empty() {
268 (0..self.column_types.len()).collect()
269 } else {
270 key.clone()
271 }
272 } else {
273 (0..self.column_types.len()).collect()
274 }
275 }
276
277 pub fn columns(&self) -> &[SqlColumnType] {
279 &self.column_types
280 }
281
282 pub fn backport_nullability_and_keys(&mut self, backport_typ: &ReprRelationType) {
286 assert_eq!(
287 backport_typ.column_types.len(),
288 self.column_types.len(),
289 "HIR and MIR types should have the same number of columns"
290 );
291 for (backport_col, sql_col) in backport_typ
292 .column_types
293 .iter()
294 .zip_eq(self.column_types.iter_mut())
295 {
296 sql_col.backport_nullability(backport_col);
297 }
298
299 self.keys = backport_typ.keys.clone();
300 }
301
302 pub fn from_repr(repr: &ReprRelationType) -> Self {
306 SqlRelationType {
307 column_types: repr
308 .column_types
309 .iter()
310 .map(SqlColumnType::from_repr)
311 .collect(),
312 keys: repr.keys.clone(),
313 }
314 }
315}
316
317impl RustType<ProtoRelationType> for SqlRelationType {
318 fn into_proto(&self) -> ProtoRelationType {
319 ProtoRelationType {
320 column_types: self.column_types.into_proto(),
321 keys: self.keys.into_proto(),
322 }
323 }
324
325 fn from_proto(proto: ProtoRelationType) -> Result<Self, TryFromProtoError> {
326 Ok(SqlRelationType {
327 column_types: proto.column_types.into_rust()?,
328 keys: proto.keys.into_rust()?,
329 })
330 }
331}
332
333impl RustType<ProtoKey> for Vec<usize> {
334 fn into_proto(&self) -> ProtoKey {
335 ProtoKey {
336 keys: self.into_proto(),
337 }
338 }
339
340 fn from_proto(proto: ProtoKey) -> Result<Self, TryFromProtoError> {
341 proto.keys.into_rust()
342 }
343}
344
345#[derive(
347 Clone,
348 Debug,
349 Eq,
350 PartialEq,
351 Ord,
352 PartialOrd,
353 Serialize,
354 Deserialize,
355 Hash
356)]
357pub struct ReprRelationType {
358 pub column_types: Vec<ReprColumnType>,
360 #[serde(default)]
370 pub keys: Vec<Vec<usize>>,
371}
372
373impl ReprRelationType {
374 pub fn empty() -> Self {
377 ReprRelationType::new(vec![])
378 }
379
380 pub fn new(column_types: Vec<ReprColumnType>) -> Self {
384 ReprRelationType {
385 column_types,
386 keys: Vec::new(),
387 }
388 }
389
390 pub fn with_key(mut self, mut indices: Vec<usize>) -> Self {
392 indices.sort_unstable();
393 if !self.keys.contains(&indices) {
394 self.keys.push(indices);
395 }
396 self
397 }
398
399 pub fn with_keys(mut self, keys: Vec<Vec<usize>>) -> Self {
400 for key in keys {
401 self = self.with_key(key)
402 }
403 self
404 }
405
406 pub fn arity(&self) -> usize {
408 self.column_types.len()
409 }
410
411 pub fn default_key(&self) -> Vec<usize> {
413 if let Some(key) = self.keys.first() {
414 if key.is_empty() {
415 (0..self.column_types.len()).collect()
416 } else {
417 key.clone()
418 }
419 } else {
420 (0..self.column_types.len()).collect()
421 }
422 }
423
424 pub fn columns(&self) -> &[ReprColumnType] {
426 &self.column_types
427 }
428}
429
430impl From<&SqlRelationType> for ReprRelationType {
431 fn from(sql_relation_type: &SqlRelationType) -> Self {
432 ReprRelationType {
433 column_types: sql_relation_type
434 .column_types
435 .iter()
436 .map(ReprColumnType::from)
437 .collect(),
438 keys: sql_relation_type.keys.clone(),
439 }
440 }
441}
442
443#[derive(
444 Clone,
445 Debug,
446 Eq,
447 PartialEq,
448 Ord,
449 PartialOrd,
450 Serialize,
451 Deserialize,
452 Hash
453)]
454pub struct ReprColumnType {
455 pub scalar_type: ReprScalarType,
457 #[serde(default = "return_true")]
459 pub nullable: bool,
460}
461
462impl std::fmt::Display for ReprColumnType {
463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464 write!(f, "{}", self.scalar_type)?;
465 if self.nullable {
466 write!(f, "?")?;
467 }
468 Ok(())
469 }
470}
471
472impl ReprColumnType {
473 pub fn union(&self, col: &ReprColumnType) -> Result<Self, anyhow::Error> {
480 let scalar_type = self.scalar_type.union(&col.scalar_type)?;
481 let nullable = self.nullable || col.nullable;
482
483 Ok(ReprColumnType {
484 scalar_type,
485 nullable,
486 })
487 }
488}
489
490impl From<&SqlColumnType> for ReprColumnType {
491 fn from(sql_column_type: &SqlColumnType) -> Self {
492 let scalar_type = &sql_column_type.scalar_type;
493 let scalar_type = scalar_type.into();
494 let nullable = sql_column_type.nullable;
495
496 ReprColumnType {
497 scalar_type,
498 nullable,
499 }
500 }
501}
502
503impl SqlColumnType {
504 pub fn from_repr(repr: &ReprColumnType) -> Self {
508 let scalar_type = &repr.scalar_type;
509 let scalar_type = SqlScalarType::from_repr(scalar_type);
510 let nullable = repr.nullable;
511
512 SqlColumnType {
513 scalar_type,
514 nullable,
515 }
516 }
517}
518
519#[derive(
521 Clone,
522 Debug,
523 Eq,
524 PartialEq,
525 Ord,
526 PartialOrd,
527 Serialize,
528 Deserialize,
529 Hash
530)]
531pub struct ColumnName(Box<str>);
532
533impl ColumnName {
534 #[inline(always)]
536 pub fn as_str(&self) -> &str {
537 &*self
538 }
539
540 pub fn as_mut_boxed_str(&mut self) -> &mut Box<str> {
542 &mut self.0
543 }
544
545 pub fn is_similar(&self, other: &ColumnName) -> bool {
547 const SIMILARITY_THRESHOLD: f64 = 0.6;
548
549 let a_lowercase = self.to_lowercase();
550 let b_lowercase = other.to_lowercase();
551
552 strsim::normalized_levenshtein(&a_lowercase, &b_lowercase) >= SIMILARITY_THRESHOLD
553 }
554}
555
556impl std::ops::Deref for ColumnName {
557 type Target = str;
558
559 #[inline(always)]
560 fn deref(&self) -> &Self::Target {
561 &self.0
562 }
563}
564
565impl fmt::Display for ColumnName {
566 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
567 f.write_str(&self.0)
568 }
569}
570
571impl From<String> for ColumnName {
572 fn from(s: String) -> ColumnName {
573 ColumnName(s.into())
574 }
575}
576
577impl From<&str> for ColumnName {
578 fn from(s: &str) -> ColumnName {
579 ColumnName(s.into())
580 }
581}
582
583impl From<&ColumnName> for ColumnName {
584 fn from(n: &ColumnName) -> ColumnName {
585 n.clone()
586 }
587}
588
589impl RustType<ProtoColumnName> for ColumnName {
590 fn into_proto(&self) -> ProtoColumnName {
591 ProtoColumnName {
592 value: Some(self.0.to_string()),
593 }
594 }
595
596 fn from_proto(proto: ProtoColumnName) -> Result<Self, TryFromProtoError> {
597 Ok(ColumnName(
598 proto
599 .value
600 .ok_or_else(|| TryFromProtoError::missing_field("ProtoColumnName::value"))?
601 .into(),
602 ))
603 }
604}
605
606#[cfg(any(test, feature = "proptest"))]
607impl proptest::arbitrary::Arbitrary for ColumnName {
608 type Parameters = ();
609 type Strategy = BoxedStrategy<ColumnName>;
610
611 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
612 let mut weights = vec![(50, Just(1..8)), (20, Just(8..16))];
615 if std::env::var("PROPTEST_LARGE_DATA").is_ok() {
616 weights.extend([
617 (5, Just(16..128)),
618 (1, Just(128..1024)),
619 (1, Just(1024..4096)),
620 ]);
621 }
622 let name_length = Union::new_weighted(weights);
623
624 let char_strat = Rc::new(Union::new_weighted(vec![
627 (50, proptest::char::range('A', 'z').boxed()),
628 (1, any::<char>().boxed()),
629 ]));
630
631 name_length
632 .prop_flat_map(move |length| proptest::collection::vec(Rc::clone(&char_strat), length))
633 .prop_map(|chars| ColumnName(chars.into_iter().collect::<Box<str>>()))
634 .no_shrink()
635 .boxed()
636 }
637}
638
639pub const UNKNOWN_COLUMN_NAME: &str = "?column?";
641
642#[derive(
644 Clone,
645 Copy,
646 Debug,
647 Eq,
648 PartialEq,
649 PartialOrd,
650 Ord,
651 Serialize,
652 Deserialize,
653 Hash
654)]
655pub struct ColumnIndex(usize);
656
657#[cfg(any(test, feature = "proptest"))]
658static_assertions::assert_not_impl_all!(ColumnIndex: Arbitrary);
659
660impl ColumnIndex {
661 pub fn to_stable_name(&self) -> String {
663 self.0.to_string()
664 }
665
666 pub fn to_raw(&self) -> usize {
667 self.0
668 }
669
670 pub fn from_raw(val: usize) -> Self {
671 ColumnIndex(val)
672 }
673}
674
675#[derive(
677 Clone,
678 Copy,
679 Debug,
680 Eq,
681 PartialEq,
682 PartialOrd,
683 Ord,
684 Serialize,
685 Deserialize,
686 Hash
687)]
688#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
689pub struct RelationVersion(u64);
690
691impl RelationVersion {
692 pub fn root() -> Self {
694 RelationVersion(0)
695 }
696
697 pub fn bump(&self) -> Self {
699 let next_version = self
700 .0
701 .checked_add(1)
702 .expect("added more than u64::MAX columns?");
703 RelationVersion(next_version)
704 }
705
706 pub fn into_raw(self) -> u64 {
710 self.0
711 }
712
713 pub fn from_raw(val: u64) -> RelationVersion {
717 RelationVersion(val)
718 }
719}
720
721impl From<RelationVersion> for SchemaId {
722 fn from(value: RelationVersion) -> Self {
723 SchemaId(usize::cast_from(value.0))
724 }
725}
726
727impl fmt::Display for RelationVersion {
728 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
729 write!(f, "v{}", self.0)
730 }
731}
732
733impl RustType<ProtoRelationVersion> for RelationVersion {
734 fn into_proto(&self) -> ProtoRelationVersion {
735 ProtoRelationVersion { value: self.0 }
736 }
737
738 fn from_proto(proto: ProtoRelationVersion) -> Result<Self, TryFromProtoError> {
739 Ok(RelationVersion(proto.value))
740 }
741}
742
743#[derive(
750 Clone,
751 Copy,
752 Debug,
753 PartialEq,
754 Eq,
755 PartialOrd,
756 Ord,
757 Hash,
758 serde::Serialize
759)]
760pub enum SemanticType {
761 CatalogItemId,
762 GlobalId,
763 ClusterId,
764 ReplicaId,
765 SchemaId,
766 DatabaseId,
767 RoleId,
768 NetworkPolicyId,
769 ShardId,
770 OID,
771 ObjectType,
772 ConnectionType,
773 SourceType,
774 MzTimestamp,
775 WallclockTimestamp,
776 ByteCount,
777 RecordCount,
778 CreditRate,
779 SqlDefinition,
780 RedactedSqlDefinition,
781}
782
783impl fmt::Display for SemanticType {
784 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
785 let s = match self {
786 SemanticType::CatalogItemId => "CatalogItemId",
787 SemanticType::GlobalId => "GlobalId",
788 SemanticType::ClusterId => "ClusterId",
789 SemanticType::ReplicaId => "ReplicaId",
790 SemanticType::SchemaId => "SchemaId",
791 SemanticType::DatabaseId => "DatabaseId",
792 SemanticType::RoleId => "RoleId",
793 SemanticType::NetworkPolicyId => "NetworkPolicyId",
794 SemanticType::ShardId => "ShardId",
795 SemanticType::OID => "OID",
796 SemanticType::ObjectType => "ObjectType",
797 SemanticType::ConnectionType => "ConnectionType",
798 SemanticType::SourceType => "SourceType",
799 SemanticType::MzTimestamp => "MzTimestamp",
800 SemanticType::WallclockTimestamp => "WallclockTimestamp",
801 SemanticType::ByteCount => "ByteCount",
802 SemanticType::RecordCount => "RecordCount",
803 SemanticType::CreditRate => "CreditRate",
804 SemanticType::SqlDefinition => "SqlDefinition",
805 SemanticType::RedactedSqlDefinition => "RedactedSqlDefinition",
806 };
807 f.write_str(s)
808 }
809}
810
811#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
813struct ColumnMetadata {
814 name: ColumnName,
816 typ_idx: usize,
818 added: RelationVersion,
820 dropped: Option<RelationVersion>,
822}
823
824#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
892pub struct RelationDesc {
893 typ: SqlRelationType,
894 metadata: BTreeMap<ColumnIndex, ColumnMetadata>,
895}
896
897impl RustType<ProtoRelationDesc> for RelationDesc {
898 fn into_proto(&self) -> ProtoRelationDesc {
907 let (names, metadata): (Vec<_>, Vec<_>) = self
908 .metadata
909 .values()
910 .map(|meta| {
911 let metadata = ProtoColumnMetadata {
912 added: Some(meta.added.into_proto()),
913 dropped: meta.dropped.map(|v| v.into_proto()),
914 };
915 (meta.name.into_proto(), metadata)
916 })
917 .unzip();
918
919 let is_all_default_metadata = metadata.iter().all(|meta| {
925 meta.added == Some(RelationVersion::root().into_proto()) && meta.dropped == None
926 });
927 let metadata = if is_all_default_metadata {
928 Vec::new()
929 } else {
930 metadata
931 };
932
933 ProtoRelationDesc {
934 typ: Some(self.typ.into_proto()),
935 names,
936 metadata,
937 }
938 }
939
940 fn from_proto(proto: ProtoRelationDesc) -> Result<Self, TryFromProtoError> {
941 let typ: SqlRelationType = proto.typ.into_rust_if_some("ProtoRelationDesc::typ")?;
942
943 if proto.names.len() != typ.column_types.len() {
948 return Err(TryFromProtoError::InvalidFieldError(format!(
949 "ProtoRelationDesc: names ({}) and column_types ({}) length mismatch",
950 proto.names.len(),
951 typ.column_types.len()
952 )));
953 }
954 if let Some(key) = typ
955 .keys
956 .iter()
957 .flatten()
958 .find(|key| **key >= typ.column_types.len())
959 {
960 return Err(TryFromProtoError::InvalidFieldError(format!(
961 "ProtoRelationDesc: key index {key} out of bounds for {} columns",
962 typ.column_types.len()
963 )));
964 }
965
966 let proto_metadata: Box<dyn Iterator<Item = _>> = if proto.metadata.is_empty() {
972 let val = ProtoColumnMetadata {
973 added: Some(RelationVersion::root().into_proto()),
974 dropped: None,
975 };
976 Box::new(itertools::repeat_n(val, proto.names.len()))
977 } else {
978 if proto.names.len() != proto.metadata.len() {
982 return Err(TryFromProtoError::InvalidFieldError(format!(
983 "ProtoRelationDesc: names ({}) and metadata ({}) length mismatch",
984 proto.names.len(),
985 proto.metadata.len()
986 )));
987 }
988 Box::new(proto.metadata.into_iter())
989 };
990
991 let metadata = proto
992 .names
993 .into_iter()
994 .zip_eq(proto_metadata)
995 .enumerate()
996 .map(|(idx, (name, metadata))| {
997 let meta = ColumnMetadata {
998 name: name.into_rust()?,
999 typ_idx: idx,
1000 added: metadata.added.into_rust_if_some("ColumnMetadata::added")?,
1001 dropped: metadata.dropped.into_rust()?,
1002 };
1003 Ok::<_, TryFromProtoError>((ColumnIndex(idx), meta))
1004 })
1005 .collect::<Result<_, _>>()?;
1006
1007 Ok(RelationDesc { typ, metadata })
1008 }
1009}
1010
1011impl RelationDesc {
1012 pub fn builder() -> RelationDescBuilder {
1014 RelationDescBuilder::default()
1015 }
1016
1017 pub fn empty() -> Self {
1020 RelationDesc {
1021 typ: SqlRelationType::empty(),
1022 metadata: BTreeMap::default(),
1023 }
1024 }
1025
1026 pub fn is_empty(&self) -> bool {
1028 self == &Self::empty()
1029 }
1030
1031 pub fn len(&self) -> usize {
1033 self.typ().column_types.len()
1034 }
1035
1036 pub fn new<I, N>(typ: SqlRelationType, names: I) -> Self
1044 where
1045 I: IntoIterator<Item = N>,
1046 N: Into<ColumnName>,
1047 {
1048 let metadata: BTreeMap<_, _> = names
1049 .into_iter()
1050 .enumerate()
1051 .map(|(idx, name)| {
1052 let col_idx = ColumnIndex(idx);
1053 let metadata = ColumnMetadata {
1054 name: name.into(),
1055 typ_idx: idx,
1056 added: RelationVersion::root(),
1057 dropped: None,
1058 };
1059 (col_idx, metadata)
1060 })
1061 .collect();
1062
1063 assert_eq!(typ.column_types.len(), metadata.len());
1065
1066 RelationDesc { typ, metadata }
1067 }
1068
1069 pub fn from_names_and_types<I, T, N>(iter: I) -> Self
1070 where
1071 I: IntoIterator<Item = (N, T)>,
1072 T: Into<SqlColumnType>,
1073 N: Into<ColumnName>,
1074 {
1075 let (names, types): (Vec<_>, Vec<_>) = iter.into_iter().unzip();
1076 let types = types.into_iter().map(Into::into).collect();
1077 let typ = SqlRelationType::new(types);
1078 Self::new(typ, names)
1079 }
1080
1081 pub fn concat(mut self, other: Self) -> Self {
1091 let self_len = self.typ.column_types.len();
1092
1093 for (typ, (_col_idx, meta)) in other.typ.column_types.into_iter().zip_eq(other.metadata) {
1094 assert_eq!(meta.added, RelationVersion::root());
1095 assert_none!(meta.dropped);
1096
1097 let new_idx = self.typ.columns().len();
1098 let new_meta = ColumnMetadata {
1099 name: meta.name,
1100 typ_idx: new_idx,
1101 added: RelationVersion::root(),
1102 dropped: None,
1103 };
1104
1105 self.typ.column_types.push(typ);
1106 let prev = self.metadata.insert(ColumnIndex(new_idx), new_meta);
1107
1108 assert_eq!(self.metadata.len(), self.typ.columns().len());
1109 assert_none!(prev);
1110 }
1111
1112 for k in other.typ.keys {
1113 let k = k.into_iter().map(|idx| idx + self_len).collect();
1114 self = self.with_key(k);
1115 }
1116 self
1117 }
1118
1119 pub fn with_key(mut self, indices: Vec<usize>) -> Self {
1121 self.typ = self.typ.with_key(indices);
1122 self
1123 }
1124
1125 pub fn without_keys(mut self) -> Self {
1127 self.typ.keys.clear();
1128 self
1129 }
1130
1131 pub fn with_names<I, N>(self, names: I) -> Self
1139 where
1140 I: IntoIterator<Item = N>,
1141 N: Into<ColumnName>,
1142 {
1143 Self::new(self.typ, names)
1144 }
1145
1146 pub fn arity(&self) -> usize {
1148 self.typ.arity()
1149 }
1150
1151 pub fn typ(&self) -> &SqlRelationType {
1153 &self.typ
1154 }
1155
1156 pub fn into_typ(self) -> SqlRelationType {
1158 self.typ
1159 }
1160
1161 pub fn iter(&self) -> impl Iterator<Item = (&ColumnName, &SqlColumnType)> {
1163 self.metadata.values().map(|meta| {
1164 let typ = &self.typ.columns()[meta.typ_idx];
1165 (&meta.name, typ)
1166 })
1167 }
1168
1169 pub fn iter_types(&self) -> impl Iterator<Item = &SqlColumnType> {
1171 self.typ.column_types.iter()
1172 }
1173
1174 pub fn iter_names(&self) -> impl Iterator<Item = &ColumnName> {
1176 self.metadata.values().map(|meta| &meta.name)
1177 }
1178
1179 pub fn iter_all(&self) -> impl Iterator<Item = (&ColumnIndex, &ColumnName, &SqlColumnType)> {
1181 self.metadata.iter().map(|(col_idx, metadata)| {
1182 let col_typ = &self.typ.columns()[metadata.typ_idx];
1183 (col_idx, &metadata.name, col_typ)
1184 })
1185 }
1186
1187 pub fn iter_similar_names<'a>(
1190 &'a self,
1191 name: &'a ColumnName,
1192 ) -> impl Iterator<Item = &'a ColumnName> {
1193 self.iter_names().filter(|n| n.is_similar(name))
1194 }
1195
1196 pub fn contains_index(&self, idx: &ColumnIndex) -> bool {
1198 self.metadata.contains_key(idx)
1199 }
1200
1201 pub fn get_by_name(&self, name: &ColumnName) -> Option<(usize, &SqlColumnType)> {
1207 self.iter_names()
1208 .position(|n| n == name)
1209 .map(|i| (i, &self.typ.column_types[i]))
1210 }
1211
1212 pub fn get_name(&self, i: usize) -> &ColumnName {
1220 self.get_name_idx(&ColumnIndex(i))
1222 }
1223
1224 pub fn get_name_idx(&self, idx: &ColumnIndex) -> &ColumnName {
1230 &self.metadata.get(idx).expect("should exist").name
1231 }
1232
1233 pub fn get_name_mut(&mut self, i: usize) -> &mut ColumnName {
1239 &mut self
1241 .metadata
1242 .get_mut(&ColumnIndex(i))
1243 .expect("should exist")
1244 .name
1245 }
1246
1247 pub fn get_type(&self, idx: &ColumnIndex) -> &SqlColumnType {
1253 let typ_idx = self.metadata.get(idx).expect("should exist").typ_idx;
1254 &self.typ.column_types[typ_idx]
1255 }
1256
1257 pub fn get_unambiguous_name(&self, i: usize) -> Option<&ColumnName> {
1266 let name = self.get_name(i);
1267 if self.iter_names().filter(|n| *n == name).count() == 1 {
1268 Some(name)
1269 } else {
1270 None
1271 }
1272 }
1273
1274 pub fn constraints_met(&self, i: usize, d: &Datum) -> Result<(), NotNullViolation> {
1279 let name = self.get_name(i);
1280 let typ = &self.typ.column_types[i];
1281 if d == &Datum::Null && !typ.nullable {
1282 Err(NotNullViolation(name.clone()))
1283 } else {
1284 Ok(())
1285 }
1286 }
1287
1288 pub fn diff(&self, other: &RelationDesc) -> RelationDescDiff {
1303 assert_eq!(self.metadata.len(), self.typ.columns().len());
1304 assert_eq!(other.metadata.len(), other.typ.columns().len());
1305 for (idx, meta) in self.metadata.iter().chain(other.metadata.iter()) {
1306 assert_eq!(meta.typ_idx, idx.0);
1307 assert_eq!(meta.added, RelationVersion::root());
1308 assert_none!(meta.dropped);
1309 }
1310
1311 let mut column_diffs = BTreeMap::new();
1312 let mut key_diff = None;
1313
1314 let left_arity = self.arity();
1315 let right_arity = other.arity();
1316 let common_arity = std::cmp::min(left_arity, right_arity);
1317
1318 for idx in 0..common_arity {
1319 let left_name = self.get_name(idx);
1320 let right_name = other.get_name(idx);
1321 let left_type = &self.typ.column_types[idx];
1322 let right_type = &other.typ.column_types[idx];
1323
1324 if left_name != right_name {
1325 let diff = ColumnDiff::NameMismatch {
1326 left: left_name.clone(),
1327 right: right_name.clone(),
1328 };
1329 column_diffs.insert(idx, diff);
1330 } else if left_type.scalar_type != right_type.scalar_type {
1331 let diff = ColumnDiff::TypeMismatch {
1332 name: left_name.clone(),
1333 left: left_type.scalar_type.clone(),
1334 right: right_type.scalar_type.clone(),
1335 };
1336 column_diffs.insert(idx, diff);
1337 } else if left_type.nullable != right_type.nullable {
1338 let diff = ColumnDiff::NullabilityMismatch {
1339 name: left_name.clone(),
1340 left: left_type.nullable,
1341 right: right_type.nullable,
1342 };
1343 column_diffs.insert(idx, diff);
1344 }
1345 }
1346
1347 for idx in common_arity..left_arity {
1348 let diff = ColumnDiff::Missing {
1349 name: self.get_name(idx).clone(),
1350 };
1351 column_diffs.insert(idx, diff);
1352 }
1353
1354 for idx in common_arity..right_arity {
1355 let diff = ColumnDiff::Extra {
1356 name: other.get_name(idx).clone(),
1357 };
1358 column_diffs.insert(idx, diff);
1359 }
1360
1361 let left_keys: BTreeSet<_> = self.typ.keys.iter().collect();
1362 let right_keys: BTreeSet<_> = other.typ.keys.iter().collect();
1363 if left_keys != right_keys {
1364 let column_names = |desc: &RelationDesc, keys: BTreeSet<&Vec<usize>>| {
1365 keys.iter()
1366 .map(|key| key.iter().map(|&idx| desc.get_name(idx).clone()).collect())
1367 .collect()
1368 };
1369 key_diff = Some(KeyDiff {
1370 left: column_names(self, left_keys),
1371 right: column_names(other, right_keys),
1372 });
1373 }
1374
1375 RelationDescDiff {
1376 column_diffs,
1377 key_diff,
1378 }
1379 }
1380
1381 pub fn apply_demand(&self, demands: &BTreeSet<usize>) -> RelationDesc {
1383 debug_assert!(
1389 self.metadata
1390 .iter()
1391 .enumerate()
1392 .all(|(pos, (idx, meta))| idx.0 == pos && meta.typ_idx == pos),
1393 "apply_demand requires a dense RelationDesc (ColumnIndex == typ_idx): {:?}",
1394 self.metadata,
1395 );
1396 let mut new_desc = self.clone();
1397
1398 let mut removed = 0;
1400 new_desc.metadata.retain(|idx, metadata| {
1401 let retain = demands.contains(&idx.0);
1402 if !retain {
1403 removed += 1;
1404 } else {
1405 metadata.typ_idx -= removed;
1406 }
1407 retain
1408 });
1409
1410 let mut idx = 0;
1412 new_desc.typ.column_types.retain(|_| {
1413 let keep = demands.contains(&idx);
1414 idx += 1;
1415 keep
1416 });
1417
1418 new_desc
1419 }
1420}
1421
1422#[cfg(any(test, feature = "proptest"))]
1423impl Arbitrary for RelationDesc {
1424 type Parameters = ();
1425 type Strategy = BoxedStrategy<RelationDesc>;
1426
1427 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1428 let mut weights = vec![(100, Just(0..4)), (50, Just(4..8)), (25, Just(8..16))];
1429 if std::env::var("PROPTEST_LARGE_DATA").is_ok() {
1430 weights.extend([
1431 (12, Just(16..32)),
1432 (6, Just(32..64)),
1433 (3, Just(64..128)),
1434 (1, Just(128..256)),
1435 ]);
1436 }
1437 let num_columns = Union::new_weighted(weights);
1438
1439 num_columns.prop_flat_map(arb_relation_desc).boxed()
1440 }
1441}
1442
1443#[cfg(any(test, feature = "proptest"))]
1446pub fn arb_relation_desc(num_cols: std::ops::Range<usize>) -> impl Strategy<Value = RelationDesc> {
1447 proptest::collection::btree_map(any::<ColumnName>(), any::<SqlColumnType>(), num_cols)
1448 .prop_map(RelationDesc::from_names_and_types)
1449}
1450
1451#[cfg(any(test, feature = "proptest"))]
1453pub fn arb_relation_desc_projection(desc: RelationDesc) -> impl Strategy<Value = RelationDesc> {
1454 let mask: Vec<_> = (0..desc.len()).map(|_| any::<bool>()).collect();
1455 mask.prop_map(move |mask| {
1456 let demands: BTreeSet<_> = mask
1457 .into_iter()
1458 .enumerate()
1459 .filter_map(|(idx, keep)| keep.then_some(idx))
1460 .collect();
1461 desc.apply_demand(&demands)
1462 })
1463}
1464
1465impl IntoIterator for RelationDesc {
1466 type Item = (ColumnName, SqlColumnType);
1467 type IntoIter = Box<dyn Iterator<Item = (ColumnName, SqlColumnType)>>;
1468
1469 fn into_iter(self) -> Self::IntoIter {
1470 let iter = self
1471 .metadata
1472 .into_values()
1473 .zip_eq(self.typ.column_types)
1474 .map(|(meta, typ)| (meta.name, typ));
1475 Box::new(iter)
1476 }
1477}
1478
1479#[cfg(any(test, feature = "proptest"))]
1481pub fn arb_row_for_relation(desc: &RelationDesc) -> impl Strategy<Value = Row> + use<> {
1482 let datums: Vec<_> = desc
1483 .typ()
1484 .columns()
1485 .iter()
1486 .cloned()
1487 .map(arb_datum_for_column)
1488 .collect();
1489 datums.prop_map(|x| Row::pack(x.iter().map(Datum::from)))
1490}
1491
1492#[derive(Debug, PartialEq, Eq)]
1494pub struct NotNullViolation(pub ColumnName);
1495
1496impl fmt::Display for NotNullViolation {
1497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1498 write!(
1499 f,
1500 "null value in column {} violates not-null constraint",
1501 self.0.quoted()
1502 )
1503 }
1504}
1505
1506#[derive(Debug, Clone, PartialEq, Eq)]
1508pub struct RelationDescDiff {
1509 pub column_diffs: BTreeMap<usize, ColumnDiff>,
1511 pub key_diff: Option<KeyDiff>,
1513}
1514
1515impl RelationDescDiff {
1516 pub fn is_empty(&self) -> bool {
1518 self.column_diffs.is_empty() && self.key_diff.is_none()
1519 }
1520}
1521
1522#[derive(Debug, Clone, PartialEq, Eq)]
1524pub enum ColumnDiff {
1525 Missing { name: ColumnName },
1527 Extra { name: ColumnName },
1529 TypeMismatch {
1531 name: ColumnName,
1532 left: SqlScalarType,
1533 right: SqlScalarType,
1534 },
1535 NullabilityMismatch {
1537 name: ColumnName,
1538 left: bool,
1539 right: bool,
1540 },
1541 NameMismatch { left: ColumnName, right: ColumnName },
1543}
1544
1545#[derive(Debug, Clone, PartialEq, Eq)]
1547pub struct KeyDiff {
1548 pub left: BTreeSet<Vec<ColumnName>>,
1550 pub right: BTreeSet<Vec<ColumnName>>,
1552}
1553
1554#[derive(Clone, Default, Debug, PartialEq, Eq)]
1556pub struct RelationDescBuilder {
1557 columns: Vec<(ColumnName, SqlColumnType)>,
1559 keys: Vec<Vec<usize>>,
1561}
1562
1563impl RelationDescBuilder {
1564 pub fn with_column<N: Into<ColumnName>>(
1566 mut self,
1567 name: N,
1568 ty: SqlColumnType,
1569 ) -> RelationDescBuilder {
1570 let name = name.into();
1571 self.columns.push((name, ty));
1572 self
1573 }
1574
1575 pub fn with_columns<I, T, N>(mut self, iter: I) -> Self
1577 where
1578 I: IntoIterator<Item = (N, T)>,
1579 T: Into<SqlColumnType>,
1580 N: Into<ColumnName>,
1581 {
1582 self.columns
1583 .extend(iter.into_iter().map(|(name, ty)| (name.into(), ty.into())));
1584 self
1585 }
1586
1587 pub fn with_key(mut self, mut indices: Vec<usize>) -> RelationDescBuilder {
1589 indices.sort_unstable();
1590 if !self.keys.contains(&indices) {
1591 self.keys.push(indices);
1592 }
1593 self
1594 }
1595
1596 pub fn without_keys(mut self) -> RelationDescBuilder {
1598 self.keys.clear();
1599 assert_eq!(self.keys.len(), 0);
1600 self
1601 }
1602
1603 pub fn concat(mut self, other: Self) -> Self {
1605 let self_len = self.columns.len();
1606
1607 self.columns.extend(other.columns);
1608 for k in other.keys {
1609 let k = k.into_iter().map(|idx| idx + self_len).collect();
1610 self = self.with_key(k);
1611 }
1612
1613 self
1614 }
1615
1616 pub fn finish(self) -> RelationDesc {
1618 let mut desc = RelationDesc::from_names_and_types(self.columns);
1619 desc.typ = desc.typ.with_keys(self.keys);
1620 desc
1621 }
1622}
1623
1624#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)]
1626pub enum RelationVersionSelector {
1627 Specific(RelationVersion),
1628 Latest,
1629}
1630
1631impl RelationVersionSelector {
1632 pub fn specific(version: u64) -> Self {
1633 RelationVersionSelector::Specific(RelationVersion(version))
1634 }
1635}
1636
1637#[derive(Debug, Clone, Serialize)]
1643pub struct VersionedRelationDesc {
1644 inner: RelationDesc,
1645}
1646
1647impl VersionedRelationDesc {
1648 pub fn new(inner: RelationDesc) -> Self {
1649 VersionedRelationDesc { inner }
1650 }
1651
1652 #[must_use]
1660 pub fn add_column<N, T>(&mut self, name: N, typ: T) -> RelationVersion
1661 where
1662 N: Into<ColumnName>,
1663 T: Into<SqlColumnType>,
1664 {
1665 let latest_version = self.latest_version();
1666 let new_version = latest_version.bump();
1667
1668 let name = name.into();
1669 let existing = self
1670 .inner
1671 .metadata
1672 .iter()
1673 .find(|(_, meta)| meta.name == name && meta.dropped.is_none());
1674 if let Some(existing) = existing {
1675 panic!("column named '{name}' already exists! {existing:?}");
1676 }
1677
1678 let next_idx = self.inner.metadata.len();
1679 let col_meta = ColumnMetadata {
1680 name,
1681 typ_idx: next_idx,
1682 added: new_version,
1683 dropped: None,
1684 };
1685
1686 self.inner.typ.column_types.push(typ.into());
1687 let prev = self.inner.metadata.insert(ColumnIndex(next_idx), col_meta);
1688
1689 assert_none!(prev, "column index overlap!");
1690 self.validate();
1691
1692 new_version
1693 }
1694
1695 #[must_use]
1704 pub fn drop_column<N>(&mut self, name: N) -> RelationVersion
1705 where
1706 N: Into<ColumnName>,
1707 {
1708 let name = name.into();
1709 let latest_version = self.latest_version();
1710 let new_version = latest_version.bump();
1711
1712 let col = self
1713 .inner
1714 .metadata
1715 .values_mut()
1716 .find(|meta| meta.name == name && meta.dropped.is_none())
1717 .expect("column to exist");
1718
1719 assert_none!(col.dropped, "column was already dropped");
1721 col.dropped = Some(new_version);
1722
1723 let dropped_key = self
1725 .inner
1726 .typ
1727 .keys
1728 .iter()
1729 .any(|keys| keys.contains(&col.typ_idx));
1730 assert!(!dropped_key, "column being dropped was used as a key");
1731
1732 self.validate();
1733 new_version
1734 }
1735
1736 pub fn latest(&self) -> RelationDesc {
1738 self.inner.clone()
1739 }
1740
1741 pub fn at_version(&self, version: RelationVersionSelector) -> RelationDesc {
1743 let up_to_version = match version {
1745 RelationVersionSelector::Latest => RelationVersion(u64::MAX),
1746 RelationVersionSelector::Specific(v) => v,
1747 };
1748
1749 let valid_columns = self.inner.metadata.iter().filter(|(_col_idx, meta)| {
1750 let added = meta.added <= up_to_version;
1751 let dropped = meta
1752 .dropped
1753 .map(|dropped_at| up_to_version >= dropped_at)
1754 .unwrap_or(false);
1755
1756 added && !dropped
1757 });
1758
1759 let mut column_types = Vec::new();
1760 let mut column_metas = BTreeMap::new();
1761
1762 for (col_idx, meta) in valid_columns {
1769 let new_meta = ColumnMetadata {
1770 name: meta.name.clone(),
1771 typ_idx: column_types.len(),
1772 added: meta.added.clone(),
1773 dropped: meta.dropped.clone(),
1774 };
1775 column_types.push(self.inner.typ.columns()[meta.typ_idx].clone());
1776 column_metas.insert(*col_idx, new_meta);
1777 }
1778
1779 let keys = self
1785 .inner
1786 .typ
1787 .keys
1788 .iter()
1789 .map(|keys| {
1790 keys.iter()
1791 .map(|key_idx| {
1792 let metadata = column_metas
1793 .get(&ColumnIndex(*key_idx))
1794 .expect("found key for column that doesn't exist");
1795 metadata.typ_idx
1796 })
1797 .collect()
1798 })
1799 .collect();
1800
1801 let relation_type = SqlRelationType { column_types, keys };
1802
1803 RelationDesc {
1804 typ: relation_type,
1805 metadata: column_metas,
1806 }
1807 }
1808
1809 pub fn latest_version(&self) -> RelationVersion {
1810 self.inner
1811 .metadata
1812 .values()
1813 .map(|meta| meta.dropped.unwrap_or(meta.added))
1815 .max()
1816 .unwrap_or_else(RelationVersion::root)
1818 }
1819
1820 fn validate(&self) {
1826 fn validate_inner(desc: &RelationDesc) -> Result<(), anyhow::Error> {
1827 if desc.typ.column_types.len() != desc.metadata.len() {
1828 anyhow::bail!("mismatch between number of types and metadatas");
1829 }
1830
1831 for (col_idx, meta) in &desc.metadata {
1832 if col_idx.0 > desc.metadata.len() {
1833 anyhow::bail!("column index out of bounds");
1834 }
1835 if meta.added >= meta.dropped.unwrap_or(RelationVersion(u64::MAX)) {
1836 anyhow::bail!("column was added after it was dropped?");
1837 }
1838 if desc.typ().columns().get(meta.typ_idx).is_none() {
1839 anyhow::bail!("typ_idx incorrect");
1840 }
1841 }
1842
1843 for keys in &desc.typ.keys {
1844 for key in keys {
1845 if *key >= desc.typ.column_types.len() {
1846 anyhow::bail!("key index was out of bounds!");
1847 }
1848 }
1849 }
1850
1851 let versions = desc
1857 .metadata
1858 .values()
1859 .flat_map(|meta| [Some(meta.added), meta.dropped])
1860 .flatten()
1861 .filter(|version| *version != RelationVersion::root());
1863 let mut max = 0;
1864 let mut sum = 0;
1865 for version in versions {
1866 max = std::cmp::max(max, version.0);
1867 sum += version.0;
1868 }
1869
1870 if sum != (max * (max + 1) / 2) {
1880 anyhow::bail!("there is a duplicate or missing relation version");
1881 }
1882
1883 Ok(())
1884 }
1885
1886 assert_ok!(validate_inner(&self.inner), "validate failed! {self:?}");
1887 }
1888}
1889
1890#[derive(Debug)]
1893#[cfg(any(test, feature = "proptest"))]
1894pub enum PropRelationDescDiff {
1895 AddColumn {
1896 name: ColumnName,
1897 typ: SqlColumnType,
1898 },
1899 DropColumn {
1900 name: ColumnName,
1901 },
1902 ToggleNullability {
1903 name: ColumnName,
1904 },
1905 ChangeType {
1906 name: ColumnName,
1907 typ: SqlColumnType,
1908 },
1909}
1910
1911#[cfg(any(test, feature = "proptest"))]
1912impl PropRelationDescDiff {
1913 pub fn apply(self, desc: &mut RelationDesc) {
1914 match self {
1915 PropRelationDescDiff::AddColumn { name, typ } => {
1916 let new_idx = desc.metadata.len();
1917 let meta = ColumnMetadata {
1918 name,
1919 typ_idx: new_idx,
1920 added: RelationVersion(0),
1921 dropped: None,
1922 };
1923 let prev = desc.metadata.insert(ColumnIndex(new_idx), meta);
1924 desc.typ.column_types.push(typ);
1925
1926 assert_none!(prev);
1927 assert_eq!(desc.metadata.len(), desc.typ.column_types.len());
1928 }
1929 PropRelationDescDiff::DropColumn { name } => {
1930 let next_version = desc
1931 .metadata
1932 .values()
1933 .map(|meta| meta.dropped.unwrap_or(meta.added))
1934 .max()
1935 .unwrap_or_else(RelationVersion::root)
1936 .bump();
1937 let Some(metadata) = desc.metadata.values_mut().find(|meta| meta.name == name)
1938 else {
1939 return;
1940 };
1941 if metadata.dropped.is_none() {
1942 metadata.dropped = Some(next_version);
1943 }
1944 }
1945 PropRelationDescDiff::ToggleNullability { name } => {
1946 let Some((pos, _)) = desc.get_by_name(&name) else {
1947 return;
1948 };
1949 let col_type = desc
1950 .typ
1951 .column_types
1952 .get_mut(pos)
1953 .expect("ColumnNames and SqlColumnTypes out of sync!");
1954 col_type.nullable = !col_type.nullable;
1955 }
1956 PropRelationDescDiff::ChangeType { name, typ } => {
1957 let Some((pos, _)) = desc.get_by_name(&name) else {
1958 return;
1959 };
1960 let col_type = desc
1961 .typ
1962 .column_types
1963 .get_mut(pos)
1964 .expect("ColumnNames and SqlColumnTypes out of sync!");
1965 *col_type = typ;
1966 }
1967 }
1968 }
1969}
1970
1971#[cfg(any(test, feature = "proptest"))]
1973pub fn arb_relation_desc_diff(
1974 source: &RelationDesc,
1975) -> impl Strategy<Value = Vec<PropRelationDescDiff>> + use<> {
1976 let source = Rc::new(source.clone());
1977 let num_source_columns = source.typ.columns().len();
1978
1979 let num_add_columns = Union::new_weighted(vec![(100, Just(0..8)), (1, Just(8..64))]);
1980 let add_columns_strat = num_add_columns
1981 .prop_flat_map(|num_columns| {
1982 proptest::collection::vec((any::<ColumnName>(), any::<SqlColumnType>()), num_columns)
1983 })
1984 .prop_map(|cols| {
1985 cols.into_iter()
1986 .map(|(name, typ)| PropRelationDescDiff::AddColumn { name, typ })
1987 .collect::<Vec<_>>()
1988 });
1989
1990 if num_source_columns == 0 {
1992 return add_columns_strat.boxed();
1993 }
1994
1995 let source_ = Rc::clone(&source);
1996 let drop_columns_strat = (0..num_source_columns).prop_perturb(move |num_columns, mut rng| {
1997 let mut set = BTreeSet::default();
1998 for _ in 0..num_columns {
1999 let col_idx = rng.random_range(0..num_source_columns);
2000 set.insert(source_.get_name(col_idx).clone());
2001 }
2002 set.into_iter()
2003 .map(|name| PropRelationDescDiff::DropColumn { name })
2004 .collect::<Vec<_>>()
2005 });
2006
2007 let source_ = Rc::clone(&source);
2008 let toggle_nullability_strat =
2009 (0..num_source_columns).prop_perturb(move |num_columns, mut rng| {
2010 let mut set = BTreeSet::default();
2011 for _ in 0..num_columns {
2012 let col_idx = rng.random_range(0..num_source_columns);
2013 set.insert(source_.get_name(col_idx).clone());
2014 }
2015 set.into_iter()
2016 .map(|name| PropRelationDescDiff::ToggleNullability { name })
2017 .collect::<Vec<_>>()
2018 });
2019
2020 let source_ = Rc::clone(&source);
2021 let change_type_strat = (0..num_source_columns)
2022 .prop_perturb(move |num_columns, mut rng| {
2023 let mut set = BTreeSet::default();
2024 for _ in 0..num_columns {
2025 let col_idx = rng.random_range(0..num_source_columns);
2026 set.insert(source_.get_name(col_idx).clone());
2027 }
2028 set
2029 })
2030 .prop_flat_map(|cols| {
2031 proptest::collection::vec(any::<SqlColumnType>(), cols.len())
2032 .prop_map(move |types| (cols.clone(), types))
2033 })
2034 .prop_map(|(cols, types)| {
2035 cols.into_iter()
2036 .zip_eq(types)
2037 .map(|(name, typ)| PropRelationDescDiff::ChangeType { name, typ })
2038 .collect::<Vec<_>>()
2039 });
2040
2041 (
2042 add_columns_strat,
2043 drop_columns_strat,
2044 toggle_nullability_strat,
2045 change_type_strat,
2046 )
2047 .prop_map(|(adds, drops, toggles, changes)| {
2048 adds.into_iter()
2049 .chain(drops)
2050 .chain(toggles)
2051 .chain(changes)
2052 .collect::<Vec<_>>()
2053 })
2054 .prop_shuffle()
2055 .boxed()
2056}
2057
2058#[cfg(test)]
2059mod tests {
2060 use super::*;
2061 use prost::Message;
2062
2063 #[mz_ore::test]
2067 #[should_panic(expected = "dense RelationDesc")]
2068 fn apply_demand_rejects_non_dense_desc() {
2069 let desc = RelationDesc::builder()
2070 .with_column("a", SqlScalarType::Int32.nullable(false))
2071 .with_column("b", SqlScalarType::Int32.nullable(false))
2072 .with_column("c", SqlScalarType::Int32.nullable(false))
2073 .finish();
2074 let mut versioned = VersionedRelationDesc::new(desc);
2075 let version = versioned.drop_column("b");
2076 let desc = versioned.at_version(RelationVersionSelector::Specific(version));
2077 let _ = desc.apply_demand(&BTreeSet::from([0]));
2078 }
2079
2080 #[mz_ore::test]
2081 #[cfg_attr(miri, ignore)] fn smoktest_at_version() {
2083 let desc = RelationDesc::builder()
2084 .with_column("a", SqlScalarType::Bool.nullable(true))
2085 .with_column("z", SqlScalarType::String.nullable(false))
2086 .finish();
2087
2088 let mut versioned_desc = VersionedRelationDesc {
2089 inner: desc.clone(),
2090 };
2091 versioned_desc.validate();
2092
2093 let latest = versioned_desc.at_version(RelationVersionSelector::Latest);
2094 assert_eq!(desc, latest);
2095
2096 let v0 = versioned_desc.at_version(RelationVersionSelector::specific(0));
2097 assert_eq!(desc, v0);
2098
2099 let v3 = versioned_desc.at_version(RelationVersionSelector::specific(3));
2100 assert_eq!(desc, v3);
2101
2102 let v1 = versioned_desc.add_column("b", SqlScalarType::Bytes.nullable(false));
2103 assert_eq!(v1, RelationVersion(1));
2104
2105 let v1 = versioned_desc.at_version(RelationVersionSelector::Specific(v1));
2106 insta::assert_json_snapshot!(v1.metadata, @r###"
2107 {
2108 "0": {
2109 "name": "a",
2110 "typ_idx": 0,
2111 "added": 0,
2112 "dropped": null
2113 },
2114 "1": {
2115 "name": "z",
2116 "typ_idx": 1,
2117 "added": 0,
2118 "dropped": null
2119 },
2120 "2": {
2121 "name": "b",
2122 "typ_idx": 2,
2123 "added": 1,
2124 "dropped": null
2125 }
2126 }
2127 "###);
2128
2129 let v0_b = versioned_desc.at_version(RelationVersionSelector::specific(0));
2131 assert!(v0.iter().eq(v0_b.iter()));
2132
2133 let v2 = versioned_desc.drop_column("z");
2134 assert_eq!(v2, RelationVersion(2));
2135
2136 let v2 = versioned_desc.at_version(RelationVersionSelector::Specific(v2));
2137 insta::assert_json_snapshot!(v2.metadata, @r###"
2138 {
2139 "0": {
2140 "name": "a",
2141 "typ_idx": 0,
2142 "added": 0,
2143 "dropped": null
2144 },
2145 "2": {
2146 "name": "b",
2147 "typ_idx": 1,
2148 "added": 1,
2149 "dropped": null
2150 }
2151 }
2152 "###);
2153
2154 let v0_c = versioned_desc.at_version(RelationVersionSelector::specific(0));
2156 assert!(v0.iter().eq(v0_c.iter()));
2157
2158 let v1_b = versioned_desc.at_version(RelationVersionSelector::specific(1));
2159 assert!(v1.iter().eq(v1_b.iter()));
2160
2161 insta::assert_json_snapshot!(versioned_desc.inner.metadata, @r###"
2162 {
2163 "0": {
2164 "name": "a",
2165 "typ_idx": 0,
2166 "added": 0,
2167 "dropped": null
2168 },
2169 "1": {
2170 "name": "z",
2171 "typ_idx": 1,
2172 "added": 0,
2173 "dropped": 2
2174 },
2175 "2": {
2176 "name": "b",
2177 "typ_idx": 2,
2178 "added": 1,
2179 "dropped": null
2180 }
2181 }
2182 "###);
2183 }
2184
2185 #[mz_ore::test]
2186 #[cfg_attr(miri, ignore)] fn test_dropping_columns_with_keys() {
2188 let desc = RelationDesc::builder()
2189 .with_column("a", SqlScalarType::Bool.nullable(true))
2190 .with_column("z", SqlScalarType::String.nullable(false))
2191 .with_key(vec![1])
2192 .finish();
2193
2194 let mut versioned_desc = VersionedRelationDesc {
2195 inner: desc.clone(),
2196 };
2197 versioned_desc.validate();
2198
2199 let v1 = versioned_desc.drop_column("a");
2200 assert_eq!(v1, RelationVersion(1));
2201
2202 let v1 = versioned_desc.at_version(RelationVersionSelector::Specific(v1));
2204 insta::assert_json_snapshot!(v1, @r###"
2205 {
2206 "typ": {
2207 "column_types": [
2208 {
2209 "scalar_type": "String",
2210 "nullable": false
2211 }
2212 ],
2213 "keys": [
2214 [
2215 0
2216 ]
2217 ]
2218 },
2219 "metadata": {
2220 "1": {
2221 "name": "z",
2222 "typ_idx": 0,
2223 "added": 0,
2224 "dropped": null
2225 }
2226 }
2227 }
2228 "###);
2229
2230 let v0 = versioned_desc.at_version(RelationVersionSelector::specific(0));
2232 insta::assert_json_snapshot!(v0, @r###"
2233 {
2234 "typ": {
2235 "column_types": [
2236 {
2237 "scalar_type": "Bool",
2238 "nullable": true
2239 },
2240 {
2241 "scalar_type": "String",
2242 "nullable": false
2243 }
2244 ],
2245 "keys": [
2246 [
2247 1
2248 ]
2249 ]
2250 },
2251 "metadata": {
2252 "0": {
2253 "name": "a",
2254 "typ_idx": 0,
2255 "added": 0,
2256 "dropped": 1
2257 },
2258 "1": {
2259 "name": "z",
2260 "typ_idx": 1,
2261 "added": 0,
2262 "dropped": null
2263 }
2264 }
2265 }
2266 "###);
2267 }
2268
2269 #[mz_ore::test]
2270 #[cfg_attr(miri, ignore)] fn roundtrip_relation_desc_without_metadata() {
2272 let typ = ProtoRelationType {
2273 column_types: vec![
2274 SqlScalarType::String.nullable(false).into_proto(),
2275 SqlScalarType::Bool.nullable(true).into_proto(),
2276 ],
2277 keys: vec![],
2278 };
2279 let proto = ProtoRelationDesc {
2280 typ: Some(typ),
2281 names: vec![
2282 ColumnName("a".into()).into_proto(),
2283 ColumnName("b".into()).into_proto(),
2284 ],
2285 metadata: vec![],
2286 };
2287 let desc: RelationDesc = proto.into_rust().unwrap();
2288
2289 insta::assert_json_snapshot!(desc, @r###"
2290 {
2291 "typ": {
2292 "column_types": [
2293 {
2294 "scalar_type": "String",
2295 "nullable": false
2296 },
2297 {
2298 "scalar_type": "Bool",
2299 "nullable": true
2300 }
2301 ],
2302 "keys": []
2303 },
2304 "metadata": {
2305 "0": {
2306 "name": "a",
2307 "typ_idx": 0,
2308 "added": 0,
2309 "dropped": null
2310 },
2311 "1": {
2312 "name": "b",
2313 "typ_idx": 1,
2314 "added": 0,
2315 "dropped": null
2316 }
2317 }
2318 }
2319 "###);
2320 }
2321
2322 #[mz_ore::test]
2323 #[should_panic(expected = "column named 'a' already exists!")]
2324 fn test_add_column_with_same_name_panics() {
2325 let desc = RelationDesc::builder()
2326 .with_column("a", SqlScalarType::Bool.nullable(true))
2327 .finish();
2328 let mut versioned = VersionedRelationDesc::new(desc);
2329
2330 let _ = versioned.add_column("a", SqlScalarType::String.nullable(false));
2331 }
2332
2333 #[mz_ore::test]
2334 #[cfg_attr(miri, ignore)] fn test_add_column_with_same_name_prev_dropped() {
2336 let desc = RelationDesc::builder()
2337 .with_column("a", SqlScalarType::Bool.nullable(true))
2338 .finish();
2339 let mut versioned = VersionedRelationDesc::new(desc);
2340
2341 let v1 = versioned.drop_column("a");
2342 let v1 = versioned.at_version(RelationVersionSelector::Specific(v1));
2343 insta::assert_json_snapshot!(v1, @r###"
2344 {
2345 "typ": {
2346 "column_types": [],
2347 "keys": []
2348 },
2349 "metadata": {}
2350 }
2351 "###);
2352
2353 let v2 = versioned.add_column("a", SqlScalarType::String.nullable(false));
2354 let v2 = versioned.at_version(RelationVersionSelector::Specific(v2));
2355 insta::assert_json_snapshot!(v2, @r###"
2356 {
2357 "typ": {
2358 "column_types": [
2359 {
2360 "scalar_type": "String",
2361 "nullable": false
2362 }
2363 ],
2364 "keys": []
2365 },
2366 "metadata": {
2367 "1": {
2368 "name": "a",
2369 "typ_idx": 0,
2370 "added": 2,
2371 "dropped": null
2372 }
2373 }
2374 }
2375 "###);
2376 }
2377
2378 #[mz_ore::test]
2379 #[cfg_attr(miri, ignore)] fn test_drop_column_added_after_root() {
2381 let desc = RelationDesc::builder()
2382 .with_column("a", SqlScalarType::Bool.nullable(true))
2383 .finish();
2384 let mut versioned = VersionedRelationDesc::new(desc);
2385
2386 let v1 = versioned.add_column("b", SqlScalarType::String.nullable(false));
2387 let v2 = versioned.drop_column("b");
2388 assert_eq!(v1, RelationVersion(1));
2389 assert_eq!(v2, RelationVersion(2));
2390
2391 assert_eq!(
2392 versioned
2393 .at_version(RelationVersionSelector::Specific(v1))
2394 .arity(),
2395 2
2396 );
2397 assert_eq!(
2398 versioned
2399 .at_version(RelationVersionSelector::Specific(v2))
2400 .arity(),
2401 1
2402 );
2403 }
2404
2405 #[mz_ore::test]
2406 #[cfg_attr(miri, ignore)] fn relation_desc_proto_rejects_corrupt_shapes() {
2408 fn proto(num_types: usize, num_names: usize, keys: Vec<Vec<usize>>) -> ProtoRelationDesc {
2409 let mut typ = SqlRelationType::new(vec![SqlScalarType::Bool.nullable(true); num_types]);
2410 typ.keys = keys;
2411 ProtoRelationDesc {
2412 typ: Some(typ.into_proto()),
2413 names: (0..num_names)
2414 .map(|i| ColumnName::from(format!("c{i}")).into_proto())
2415 .collect(),
2416 metadata: vec![],
2417 }
2418 }
2419
2420 for (num_types, num_names) in [(0, 1), (2, 0), (3, 1)] {
2424 let err = RelationDesc::from_proto(proto(num_types, num_names, vec![]))
2425 .expect_err("length mismatch must be rejected");
2426 assert!(err.to_string().contains("length mismatch"), "{err}");
2427 }
2428 let err = RelationDesc::from_proto(proto(1, 1, vec![vec![7]]))
2429 .expect_err("out of bounds key must be rejected");
2430 assert!(err.to_string().contains("out of bounds"), "{err}");
2431
2432 let desc = RelationDesc::from_proto(proto(2, 2, vec![vec![1]])).expect("valid");
2434 assert_eq!(desc.iter().count(), 2);
2435 }
2436
2437 #[mz_ore::test]
2438 #[cfg_attr(miri, ignore)]
2439 fn apply_demand() {
2440 let desc = RelationDesc::builder()
2441 .with_column("a", SqlScalarType::String.nullable(true))
2442 .with_column("b", SqlScalarType::Int64.nullable(false))
2443 .with_column("c", SqlScalarType::Time.nullable(false))
2444 .finish();
2445 let desc = desc.apply_demand(&BTreeSet::from([0, 2]));
2446 assert_eq!(desc.arity(), 2);
2447 VersionedRelationDesc::new(desc).validate();
2449 }
2450
2451 #[mz_ore::test]
2452 #[cfg_attr(miri, ignore)]
2453 fn smoketest_column_index_stable_ident() {
2454 let idx_a = ColumnIndex(42);
2455 assert_eq!(idx_a.to_stable_name(), "42");
2457 }
2458
2459 #[mz_ore::test]
2460 #[cfg_attr(miri, ignore)] fn proptest_relation_desc_roundtrips() {
2462 fn testcase(og: RelationDesc) {
2463 let bytes = og.into_proto().encode_to_vec();
2464 let proto = ProtoRelationDesc::decode(&bytes[..]).unwrap();
2465 let rnd = RelationDesc::from_proto(proto).unwrap();
2466
2467 assert_eq!(og, rnd);
2468 }
2469
2470 proptest!(|(desc in any::<RelationDesc>())| {
2471 testcase(desc);
2472 });
2473
2474 let strat = any::<RelationDesc>().prop_flat_map(|desc| {
2475 arb_relation_desc_diff(&desc).prop_map(move |diffs| (desc.clone(), diffs))
2476 });
2477
2478 proptest!(|((mut desc, diffs) in strat)| {
2479 for diff in diffs {
2480 diff.apply(&mut desc);
2481 };
2482 testcase(desc);
2483 });
2484 }
2485}