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
606impl From<ColumnName> for mz_sql_parser::ast::Ident {
607 fn from(value: ColumnName) -> Self {
608 mz_sql_parser::ast::Ident::new_unchecked(value.0)
610 }
611}
612
613#[cfg(any(test, feature = "proptest"))]
614impl proptest::arbitrary::Arbitrary for ColumnName {
615 type Parameters = ();
616 type Strategy = BoxedStrategy<ColumnName>;
617
618 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
619 let mut weights = vec![(50, Just(1..8)), (20, Just(8..16))];
622 if std::env::var("PROPTEST_LARGE_DATA").is_ok() {
623 weights.extend([
624 (5, Just(16..128)),
625 (1, Just(128..1024)),
626 (1, Just(1024..4096)),
627 ]);
628 }
629 let name_length = Union::new_weighted(weights);
630
631 let char_strat = Rc::new(Union::new_weighted(vec![
634 (50, proptest::char::range('A', 'z').boxed()),
635 (1, any::<char>().boxed()),
636 ]));
637
638 name_length
639 .prop_flat_map(move |length| proptest::collection::vec(Rc::clone(&char_strat), length))
640 .prop_map(|chars| ColumnName(chars.into_iter().collect::<Box<str>>()))
641 .no_shrink()
642 .boxed()
643 }
644}
645
646pub const UNKNOWN_COLUMN_NAME: &str = "?column?";
648
649#[derive(
651 Clone,
652 Copy,
653 Debug,
654 Eq,
655 PartialEq,
656 PartialOrd,
657 Ord,
658 Serialize,
659 Deserialize,
660 Hash
661)]
662pub struct ColumnIndex(usize);
663
664#[cfg(any(test, feature = "proptest"))]
665static_assertions::assert_not_impl_all!(ColumnIndex: Arbitrary);
666
667impl ColumnIndex {
668 pub fn to_stable_name(&self) -> String {
670 self.0.to_string()
671 }
672
673 pub fn to_raw(&self) -> usize {
674 self.0
675 }
676
677 pub fn from_raw(val: usize) -> Self {
678 ColumnIndex(val)
679 }
680}
681
682#[derive(
684 Clone,
685 Copy,
686 Debug,
687 Eq,
688 PartialEq,
689 PartialOrd,
690 Ord,
691 Serialize,
692 Deserialize,
693 Hash
694)]
695#[cfg_attr(any(test, feature = "proptest"), derive(Arbitrary))]
696pub struct RelationVersion(u64);
697
698impl RelationVersion {
699 pub fn root() -> Self {
701 RelationVersion(0)
702 }
703
704 pub fn bump(&self) -> Self {
706 let next_version = self
707 .0
708 .checked_add(1)
709 .expect("added more than u64::MAX columns?");
710 RelationVersion(next_version)
711 }
712
713 pub fn into_raw(self) -> u64 {
717 self.0
718 }
719
720 pub fn from_raw(val: u64) -> RelationVersion {
724 RelationVersion(val)
725 }
726}
727
728impl From<RelationVersion> for SchemaId {
729 fn from(value: RelationVersion) -> Self {
730 SchemaId(usize::cast_from(value.0))
731 }
732}
733
734impl From<mz_sql_parser::ast::Version> for RelationVersion {
735 fn from(value: mz_sql_parser::ast::Version) -> Self {
736 RelationVersion(value.into_inner())
737 }
738}
739
740impl fmt::Display for RelationVersion {
741 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742 write!(f, "v{}", self.0)
743 }
744}
745
746impl From<RelationVersion> for mz_sql_parser::ast::Version {
747 fn from(value: RelationVersion) -> Self {
748 mz_sql_parser::ast::Version::new(value.0)
749 }
750}
751
752impl RustType<ProtoRelationVersion> for RelationVersion {
753 fn into_proto(&self) -> ProtoRelationVersion {
754 ProtoRelationVersion { value: self.0 }
755 }
756
757 fn from_proto(proto: ProtoRelationVersion) -> Result<Self, TryFromProtoError> {
758 Ok(RelationVersion(proto.value))
759 }
760}
761
762#[derive(
769 Clone,
770 Copy,
771 Debug,
772 PartialEq,
773 Eq,
774 PartialOrd,
775 Ord,
776 Hash,
777 serde::Serialize
778)]
779pub enum SemanticType {
780 CatalogItemId,
781 GlobalId,
782 ClusterId,
783 ReplicaId,
784 SchemaId,
785 DatabaseId,
786 RoleId,
787 NetworkPolicyId,
788 ShardId,
789 OID,
790 ObjectType,
791 ConnectionType,
792 SourceType,
793 MzTimestamp,
794 WallclockTimestamp,
795 ByteCount,
796 RecordCount,
797 CreditRate,
798 SqlDefinition,
799 RedactedSqlDefinition,
800}
801
802impl fmt::Display for SemanticType {
803 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804 let s = match self {
805 SemanticType::CatalogItemId => "CatalogItemId",
806 SemanticType::GlobalId => "GlobalId",
807 SemanticType::ClusterId => "ClusterId",
808 SemanticType::ReplicaId => "ReplicaId",
809 SemanticType::SchemaId => "SchemaId",
810 SemanticType::DatabaseId => "DatabaseId",
811 SemanticType::RoleId => "RoleId",
812 SemanticType::NetworkPolicyId => "NetworkPolicyId",
813 SemanticType::ShardId => "ShardId",
814 SemanticType::OID => "OID",
815 SemanticType::ObjectType => "ObjectType",
816 SemanticType::ConnectionType => "ConnectionType",
817 SemanticType::SourceType => "SourceType",
818 SemanticType::MzTimestamp => "MzTimestamp",
819 SemanticType::WallclockTimestamp => "WallclockTimestamp",
820 SemanticType::ByteCount => "ByteCount",
821 SemanticType::RecordCount => "RecordCount",
822 SemanticType::CreditRate => "CreditRate",
823 SemanticType::SqlDefinition => "SqlDefinition",
824 SemanticType::RedactedSqlDefinition => "RedactedSqlDefinition",
825 };
826 f.write_str(s)
827 }
828}
829
830#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
832struct ColumnMetadata {
833 name: ColumnName,
835 typ_idx: usize,
837 added: RelationVersion,
839 dropped: Option<RelationVersion>,
841}
842
843#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
911pub struct RelationDesc {
912 typ: SqlRelationType,
913 metadata: BTreeMap<ColumnIndex, ColumnMetadata>,
914}
915
916impl RustType<ProtoRelationDesc> for RelationDesc {
917 fn into_proto(&self) -> ProtoRelationDesc {
918 let (names, metadata): (Vec<_>, Vec<_>) = self
919 .metadata
920 .values()
921 .map(|meta| {
922 let metadata = ProtoColumnMetadata {
923 added: Some(meta.added.into_proto()),
924 dropped: meta.dropped.map(|v| v.into_proto()),
925 };
926 (meta.name.into_proto(), metadata)
927 })
928 .unzip();
929
930 let is_all_default_metadata = metadata.iter().all(|meta| {
936 meta.added == Some(RelationVersion::root().into_proto()) && meta.dropped == None
937 });
938 let metadata = if is_all_default_metadata {
939 Vec::new()
940 } else {
941 metadata
942 };
943
944 ProtoRelationDesc {
945 typ: Some(self.typ.into_proto()),
946 names,
947 metadata,
948 }
949 }
950
951 fn from_proto(proto: ProtoRelationDesc) -> Result<Self, TryFromProtoError> {
952 let proto_metadata: Box<dyn Iterator<Item = _>> = if proto.metadata.is_empty() {
958 let val = ProtoColumnMetadata {
959 added: Some(RelationVersion::root().into_proto()),
960 dropped: None,
961 };
962 Box::new(itertools::repeat_n(val, proto.names.len()))
963 } else {
964 if proto.names.len() != proto.metadata.len() {
968 return Err(TryFromProtoError::InvalidFieldError(format!(
969 "ProtoRelationDesc: names ({}) and metadata ({}) length mismatch",
970 proto.names.len(),
971 proto.metadata.len()
972 )));
973 }
974 Box::new(proto.metadata.into_iter())
975 };
976
977 let metadata = proto
978 .names
979 .into_iter()
980 .zip_eq(proto_metadata)
981 .enumerate()
982 .map(|(idx, (name, metadata))| {
983 let meta = ColumnMetadata {
984 name: name.into_rust()?,
985 typ_idx: idx,
986 added: metadata.added.into_rust_if_some("ColumnMetadata::added")?,
987 dropped: metadata.dropped.into_rust()?,
988 };
989 Ok::<_, TryFromProtoError>((ColumnIndex(idx), meta))
990 })
991 .collect::<Result<_, _>>()?;
992
993 Ok(RelationDesc {
994 typ: proto.typ.into_rust_if_some("ProtoRelationDesc::typ")?,
995 metadata,
996 })
997 }
998}
999
1000impl RelationDesc {
1001 pub fn builder() -> RelationDescBuilder {
1003 RelationDescBuilder::default()
1004 }
1005
1006 pub fn empty() -> Self {
1009 RelationDesc {
1010 typ: SqlRelationType::empty(),
1011 metadata: BTreeMap::default(),
1012 }
1013 }
1014
1015 pub fn is_empty(&self) -> bool {
1017 self == &Self::empty()
1018 }
1019
1020 pub fn len(&self) -> usize {
1022 self.typ().column_types.len()
1023 }
1024
1025 pub fn new<I, N>(typ: SqlRelationType, names: I) -> Self
1033 where
1034 I: IntoIterator<Item = N>,
1035 N: Into<ColumnName>,
1036 {
1037 let metadata: BTreeMap<_, _> = names
1038 .into_iter()
1039 .enumerate()
1040 .map(|(idx, name)| {
1041 let col_idx = ColumnIndex(idx);
1042 let metadata = ColumnMetadata {
1043 name: name.into(),
1044 typ_idx: idx,
1045 added: RelationVersion::root(),
1046 dropped: None,
1047 };
1048 (col_idx, metadata)
1049 })
1050 .collect();
1051
1052 assert_eq!(typ.column_types.len(), metadata.len());
1054
1055 RelationDesc { typ, metadata }
1056 }
1057
1058 pub fn from_names_and_types<I, T, N>(iter: I) -> Self
1059 where
1060 I: IntoIterator<Item = (N, T)>,
1061 T: Into<SqlColumnType>,
1062 N: Into<ColumnName>,
1063 {
1064 let (names, types): (Vec<_>, Vec<_>) = iter.into_iter().unzip();
1065 let types = types.into_iter().map(Into::into).collect();
1066 let typ = SqlRelationType::new(types);
1067 Self::new(typ, names)
1068 }
1069
1070 pub fn concat(mut self, other: Self) -> Self {
1080 let self_len = self.typ.column_types.len();
1081
1082 for (typ, (_col_idx, meta)) in other.typ.column_types.into_iter().zip_eq(other.metadata) {
1083 assert_eq!(meta.added, RelationVersion::root());
1084 assert_none!(meta.dropped);
1085
1086 let new_idx = self.typ.columns().len();
1087 let new_meta = ColumnMetadata {
1088 name: meta.name,
1089 typ_idx: new_idx,
1090 added: RelationVersion::root(),
1091 dropped: None,
1092 };
1093
1094 self.typ.column_types.push(typ);
1095 let prev = self.metadata.insert(ColumnIndex(new_idx), new_meta);
1096
1097 assert_eq!(self.metadata.len(), self.typ.columns().len());
1098 assert_none!(prev);
1099 }
1100
1101 for k in other.typ.keys {
1102 let k = k.into_iter().map(|idx| idx + self_len).collect();
1103 self = self.with_key(k);
1104 }
1105 self
1106 }
1107
1108 pub fn with_key(mut self, indices: Vec<usize>) -> Self {
1110 self.typ = self.typ.with_key(indices);
1111 self
1112 }
1113
1114 pub fn without_keys(mut self) -> Self {
1116 self.typ.keys.clear();
1117 self
1118 }
1119
1120 pub fn with_names<I, N>(self, names: I) -> Self
1128 where
1129 I: IntoIterator<Item = N>,
1130 N: Into<ColumnName>,
1131 {
1132 Self::new(self.typ, names)
1133 }
1134
1135 pub fn arity(&self) -> usize {
1137 self.typ.arity()
1138 }
1139
1140 pub fn typ(&self) -> &SqlRelationType {
1142 &self.typ
1143 }
1144
1145 pub fn into_typ(self) -> SqlRelationType {
1147 self.typ
1148 }
1149
1150 pub fn iter(&self) -> impl Iterator<Item = (&ColumnName, &SqlColumnType)> {
1152 self.metadata.values().map(|meta| {
1153 let typ = &self.typ.columns()[meta.typ_idx];
1154 (&meta.name, typ)
1155 })
1156 }
1157
1158 pub fn iter_types(&self) -> impl Iterator<Item = &SqlColumnType> {
1160 self.typ.column_types.iter()
1161 }
1162
1163 pub fn iter_names(&self) -> impl Iterator<Item = &ColumnName> {
1165 self.metadata.values().map(|meta| &meta.name)
1166 }
1167
1168 pub fn iter_all(&self) -> impl Iterator<Item = (&ColumnIndex, &ColumnName, &SqlColumnType)> {
1170 self.metadata.iter().map(|(col_idx, metadata)| {
1171 let col_typ = &self.typ.columns()[metadata.typ_idx];
1172 (col_idx, &metadata.name, col_typ)
1173 })
1174 }
1175
1176 pub fn iter_similar_names<'a>(
1179 &'a self,
1180 name: &'a ColumnName,
1181 ) -> impl Iterator<Item = &'a ColumnName> {
1182 self.iter_names().filter(|n| n.is_similar(name))
1183 }
1184
1185 pub fn contains_index(&self, idx: &ColumnIndex) -> bool {
1187 self.metadata.contains_key(idx)
1188 }
1189
1190 pub fn get_by_name(&self, name: &ColumnName) -> Option<(usize, &SqlColumnType)> {
1196 self.iter_names()
1197 .position(|n| n == name)
1198 .map(|i| (i, &self.typ.column_types[i]))
1199 }
1200
1201 pub fn get_name(&self, i: usize) -> &ColumnName {
1209 self.get_name_idx(&ColumnIndex(i))
1211 }
1212
1213 pub fn get_name_idx(&self, idx: &ColumnIndex) -> &ColumnName {
1219 &self.metadata.get(idx).expect("should exist").name
1220 }
1221
1222 pub fn get_name_mut(&mut self, i: usize) -> &mut ColumnName {
1228 &mut self
1230 .metadata
1231 .get_mut(&ColumnIndex(i))
1232 .expect("should exist")
1233 .name
1234 }
1235
1236 pub fn get_type(&self, idx: &ColumnIndex) -> &SqlColumnType {
1242 let typ_idx = self.metadata.get(idx).expect("should exist").typ_idx;
1243 &self.typ.column_types[typ_idx]
1244 }
1245
1246 pub fn get_unambiguous_name(&self, i: usize) -> Option<&ColumnName> {
1255 let name = self.get_name(i);
1256 if self.iter_names().filter(|n| *n == name).count() == 1 {
1257 Some(name)
1258 } else {
1259 None
1260 }
1261 }
1262
1263 pub fn constraints_met(&self, i: usize, d: &Datum) -> Result<(), NotNullViolation> {
1268 let name = self.get_name(i);
1269 let typ = &self.typ.column_types[i];
1270 if d == &Datum::Null && !typ.nullable {
1271 Err(NotNullViolation(name.clone()))
1272 } else {
1273 Ok(())
1274 }
1275 }
1276
1277 pub fn diff(&self, other: &RelationDesc) -> RelationDescDiff {
1292 assert_eq!(self.metadata.len(), self.typ.columns().len());
1293 assert_eq!(other.metadata.len(), other.typ.columns().len());
1294 for (idx, meta) in self.metadata.iter().chain(other.metadata.iter()) {
1295 assert_eq!(meta.typ_idx, idx.0);
1296 assert_eq!(meta.added, RelationVersion::root());
1297 assert_none!(meta.dropped);
1298 }
1299
1300 let mut column_diffs = BTreeMap::new();
1301 let mut key_diff = None;
1302
1303 let left_arity = self.arity();
1304 let right_arity = other.arity();
1305 let common_arity = std::cmp::min(left_arity, right_arity);
1306
1307 for idx in 0..common_arity {
1308 let left_name = self.get_name(idx);
1309 let right_name = other.get_name(idx);
1310 let left_type = &self.typ.column_types[idx];
1311 let right_type = &other.typ.column_types[idx];
1312
1313 if left_name != right_name {
1314 let diff = ColumnDiff::NameMismatch {
1315 left: left_name.clone(),
1316 right: right_name.clone(),
1317 };
1318 column_diffs.insert(idx, diff);
1319 } else if left_type.scalar_type != right_type.scalar_type {
1320 let diff = ColumnDiff::TypeMismatch {
1321 name: left_name.clone(),
1322 left: left_type.scalar_type.clone(),
1323 right: right_type.scalar_type.clone(),
1324 };
1325 column_diffs.insert(idx, diff);
1326 } else if left_type.nullable != right_type.nullable {
1327 let diff = ColumnDiff::NullabilityMismatch {
1328 name: left_name.clone(),
1329 left: left_type.nullable,
1330 right: right_type.nullable,
1331 };
1332 column_diffs.insert(idx, diff);
1333 }
1334 }
1335
1336 for idx in common_arity..left_arity {
1337 let diff = ColumnDiff::Missing {
1338 name: self.get_name(idx).clone(),
1339 };
1340 column_diffs.insert(idx, diff);
1341 }
1342
1343 for idx in common_arity..right_arity {
1344 let diff = ColumnDiff::Extra {
1345 name: other.get_name(idx).clone(),
1346 };
1347 column_diffs.insert(idx, diff);
1348 }
1349
1350 let left_keys: BTreeSet<_> = self.typ.keys.iter().collect();
1351 let right_keys: BTreeSet<_> = other.typ.keys.iter().collect();
1352 if left_keys != right_keys {
1353 let column_names = |desc: &RelationDesc, keys: BTreeSet<&Vec<usize>>| {
1354 keys.iter()
1355 .map(|key| key.iter().map(|&idx| desc.get_name(idx).clone()).collect())
1356 .collect()
1357 };
1358 key_diff = Some(KeyDiff {
1359 left: column_names(self, left_keys),
1360 right: column_names(other, right_keys),
1361 });
1362 }
1363
1364 RelationDescDiff {
1365 column_diffs,
1366 key_diff,
1367 }
1368 }
1369
1370 pub fn apply_demand(&self, demands: &BTreeSet<usize>) -> RelationDesc {
1372 let mut new_desc = self.clone();
1373
1374 let mut removed = 0;
1376 new_desc.metadata.retain(|idx, metadata| {
1377 let retain = demands.contains(&idx.0);
1378 if !retain {
1379 removed += 1;
1380 } else {
1381 metadata.typ_idx -= removed;
1382 }
1383 retain
1384 });
1385
1386 let mut idx = 0;
1388 new_desc.typ.column_types.retain(|_| {
1389 let keep = demands.contains(&idx);
1390 idx += 1;
1391 keep
1392 });
1393
1394 new_desc
1395 }
1396}
1397
1398#[cfg(any(test, feature = "proptest"))]
1399impl Arbitrary for RelationDesc {
1400 type Parameters = ();
1401 type Strategy = BoxedStrategy<RelationDesc>;
1402
1403 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
1404 let mut weights = vec![(100, Just(0..4)), (50, Just(4..8)), (25, Just(8..16))];
1405 if std::env::var("PROPTEST_LARGE_DATA").is_ok() {
1406 weights.extend([
1407 (12, Just(16..32)),
1408 (6, Just(32..64)),
1409 (3, Just(64..128)),
1410 (1, Just(128..256)),
1411 ]);
1412 }
1413 let num_columns = Union::new_weighted(weights);
1414
1415 num_columns.prop_flat_map(arb_relation_desc).boxed()
1416 }
1417}
1418
1419#[cfg(any(test, feature = "proptest"))]
1422pub fn arb_relation_desc(num_cols: std::ops::Range<usize>) -> impl Strategy<Value = RelationDesc> {
1423 proptest::collection::btree_map(any::<ColumnName>(), any::<SqlColumnType>(), num_cols)
1424 .prop_map(RelationDesc::from_names_and_types)
1425}
1426
1427#[cfg(any(test, feature = "proptest"))]
1429pub fn arb_relation_desc_projection(desc: RelationDesc) -> impl Strategy<Value = RelationDesc> {
1430 let mask: Vec<_> = (0..desc.len()).map(|_| any::<bool>()).collect();
1431 mask.prop_map(move |mask| {
1432 let demands: BTreeSet<_> = mask
1433 .into_iter()
1434 .enumerate()
1435 .filter_map(|(idx, keep)| keep.then_some(idx))
1436 .collect();
1437 desc.apply_demand(&demands)
1438 })
1439}
1440
1441impl IntoIterator for RelationDesc {
1442 type Item = (ColumnName, SqlColumnType);
1443 type IntoIter = Box<dyn Iterator<Item = (ColumnName, SqlColumnType)>>;
1444
1445 fn into_iter(self) -> Self::IntoIter {
1446 let iter = self
1447 .metadata
1448 .into_values()
1449 .zip_eq(self.typ.column_types)
1450 .map(|(meta, typ)| (meta.name, typ));
1451 Box::new(iter)
1452 }
1453}
1454
1455#[cfg(any(test, feature = "proptest"))]
1457pub fn arb_row_for_relation(desc: &RelationDesc) -> impl Strategy<Value = Row> + use<> {
1458 let datums: Vec<_> = desc
1459 .typ()
1460 .columns()
1461 .iter()
1462 .cloned()
1463 .map(arb_datum_for_column)
1464 .collect();
1465 datums.prop_map(|x| Row::pack(x.iter().map(Datum::from)))
1466}
1467
1468#[derive(Debug, PartialEq, Eq)]
1470pub struct NotNullViolation(pub ColumnName);
1471
1472impl fmt::Display for NotNullViolation {
1473 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1474 write!(
1475 f,
1476 "null value in column {} violates not-null constraint",
1477 self.0.quoted()
1478 )
1479 }
1480}
1481
1482#[derive(Debug, Clone, PartialEq, Eq)]
1484pub struct RelationDescDiff {
1485 pub column_diffs: BTreeMap<usize, ColumnDiff>,
1487 pub key_diff: Option<KeyDiff>,
1489}
1490
1491impl RelationDescDiff {
1492 pub fn is_empty(&self) -> bool {
1494 self.column_diffs.is_empty() && self.key_diff.is_none()
1495 }
1496}
1497
1498#[derive(Debug, Clone, PartialEq, Eq)]
1500pub enum ColumnDiff {
1501 Missing { name: ColumnName },
1503 Extra { name: ColumnName },
1505 TypeMismatch {
1507 name: ColumnName,
1508 left: SqlScalarType,
1509 right: SqlScalarType,
1510 },
1511 NullabilityMismatch {
1513 name: ColumnName,
1514 left: bool,
1515 right: bool,
1516 },
1517 NameMismatch { left: ColumnName, right: ColumnName },
1519}
1520
1521#[derive(Debug, Clone, PartialEq, Eq)]
1523pub struct KeyDiff {
1524 pub left: BTreeSet<Vec<ColumnName>>,
1526 pub right: BTreeSet<Vec<ColumnName>>,
1528}
1529
1530#[derive(Clone, Default, Debug, PartialEq, Eq)]
1532pub struct RelationDescBuilder {
1533 columns: Vec<(ColumnName, SqlColumnType)>,
1535 keys: Vec<Vec<usize>>,
1537}
1538
1539impl RelationDescBuilder {
1540 pub fn with_column<N: Into<ColumnName>>(
1542 mut self,
1543 name: N,
1544 ty: SqlColumnType,
1545 ) -> RelationDescBuilder {
1546 let name = name.into();
1547 self.columns.push((name, ty));
1548 self
1549 }
1550
1551 pub fn with_columns<I, T, N>(mut self, iter: I) -> Self
1553 where
1554 I: IntoIterator<Item = (N, T)>,
1555 T: Into<SqlColumnType>,
1556 N: Into<ColumnName>,
1557 {
1558 self.columns
1559 .extend(iter.into_iter().map(|(name, ty)| (name.into(), ty.into())));
1560 self
1561 }
1562
1563 pub fn with_key(mut self, mut indices: Vec<usize>) -> RelationDescBuilder {
1565 indices.sort_unstable();
1566 if !self.keys.contains(&indices) {
1567 self.keys.push(indices);
1568 }
1569 self
1570 }
1571
1572 pub fn without_keys(mut self) -> RelationDescBuilder {
1574 self.keys.clear();
1575 assert_eq!(self.keys.len(), 0);
1576 self
1577 }
1578
1579 pub fn concat(mut self, other: Self) -> Self {
1581 let self_len = self.columns.len();
1582
1583 self.columns.extend(other.columns);
1584 for k in other.keys {
1585 let k = k.into_iter().map(|idx| idx + self_len).collect();
1586 self = self.with_key(k);
1587 }
1588
1589 self
1590 }
1591
1592 pub fn finish(self) -> RelationDesc {
1594 let mut desc = RelationDesc::from_names_and_types(self.columns);
1595 desc.typ = desc.typ.with_keys(self.keys);
1596 desc
1597 }
1598}
1599
1600#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)]
1602pub enum RelationVersionSelector {
1603 Specific(RelationVersion),
1604 Latest,
1605}
1606
1607impl RelationVersionSelector {
1608 pub fn specific(version: u64) -> Self {
1609 RelationVersionSelector::Specific(RelationVersion(version))
1610 }
1611}
1612
1613#[derive(Debug, Clone, Serialize)]
1619pub struct VersionedRelationDesc {
1620 inner: RelationDesc,
1621}
1622
1623impl VersionedRelationDesc {
1624 pub fn new(inner: RelationDesc) -> Self {
1625 VersionedRelationDesc { inner }
1626 }
1627
1628 #[must_use]
1636 pub fn add_column<N, T>(&mut self, name: N, typ: T) -> RelationVersion
1637 where
1638 N: Into<ColumnName>,
1639 T: Into<SqlColumnType>,
1640 {
1641 let latest_version = self.latest_version();
1642 let new_version = latest_version.bump();
1643
1644 let name = name.into();
1645 let existing = self
1646 .inner
1647 .metadata
1648 .iter()
1649 .find(|(_, meta)| meta.name == name && meta.dropped.is_none());
1650 if let Some(existing) = existing {
1651 panic!("column named '{name}' already exists! {existing:?}");
1652 }
1653
1654 let next_idx = self.inner.metadata.len();
1655 let col_meta = ColumnMetadata {
1656 name,
1657 typ_idx: next_idx,
1658 added: new_version,
1659 dropped: None,
1660 };
1661
1662 self.inner.typ.column_types.push(typ.into());
1663 let prev = self.inner.metadata.insert(ColumnIndex(next_idx), col_meta);
1664
1665 assert_none!(prev, "column index overlap!");
1666 self.validate();
1667
1668 new_version
1669 }
1670
1671 #[must_use]
1680 pub fn drop_column<N>(&mut self, name: N) -> RelationVersion
1681 where
1682 N: Into<ColumnName>,
1683 {
1684 let name = name.into();
1685 let latest_version = self.latest_version();
1686 let new_version = latest_version.bump();
1687
1688 let col = self
1689 .inner
1690 .metadata
1691 .values_mut()
1692 .find(|meta| meta.name == name && meta.dropped.is_none())
1693 .expect("column to exist");
1694
1695 assert_none!(col.dropped, "column was already dropped");
1697 col.dropped = Some(new_version);
1698
1699 let dropped_key = self
1701 .inner
1702 .typ
1703 .keys
1704 .iter()
1705 .any(|keys| keys.contains(&col.typ_idx));
1706 assert!(!dropped_key, "column being dropped was used as a key");
1707
1708 self.validate();
1709 new_version
1710 }
1711
1712 pub fn latest(&self) -> RelationDesc {
1714 self.inner.clone()
1715 }
1716
1717 pub fn at_version(&self, version: RelationVersionSelector) -> RelationDesc {
1719 let up_to_version = match version {
1721 RelationVersionSelector::Latest => RelationVersion(u64::MAX),
1722 RelationVersionSelector::Specific(v) => v,
1723 };
1724
1725 let valid_columns = self.inner.metadata.iter().filter(|(_col_idx, meta)| {
1726 let added = meta.added <= up_to_version;
1727 let dropped = meta
1728 .dropped
1729 .map(|dropped_at| up_to_version >= dropped_at)
1730 .unwrap_or(false);
1731
1732 added && !dropped
1733 });
1734
1735 let mut column_types = Vec::new();
1736 let mut column_metas = BTreeMap::new();
1737
1738 for (col_idx, meta) in valid_columns {
1745 let new_meta = ColumnMetadata {
1746 name: meta.name.clone(),
1747 typ_idx: column_types.len(),
1748 added: meta.added.clone(),
1749 dropped: meta.dropped.clone(),
1750 };
1751 column_types.push(self.inner.typ.columns()[meta.typ_idx].clone());
1752 column_metas.insert(*col_idx, new_meta);
1753 }
1754
1755 let keys = self
1761 .inner
1762 .typ
1763 .keys
1764 .iter()
1765 .map(|keys| {
1766 keys.iter()
1767 .map(|key_idx| {
1768 let metadata = column_metas
1769 .get(&ColumnIndex(*key_idx))
1770 .expect("found key for column that doesn't exist");
1771 metadata.typ_idx
1772 })
1773 .collect()
1774 })
1775 .collect();
1776
1777 let relation_type = SqlRelationType { column_types, keys };
1778
1779 RelationDesc {
1780 typ: relation_type,
1781 metadata: column_metas,
1782 }
1783 }
1784
1785 pub fn latest_version(&self) -> RelationVersion {
1786 self.inner
1787 .metadata
1788 .values()
1789 .map(|meta| meta.dropped.unwrap_or(meta.added))
1791 .max()
1792 .unwrap_or_else(RelationVersion::root)
1794 }
1795
1796 fn validate(&self) {
1802 fn validate_inner(desc: &RelationDesc) -> Result<(), anyhow::Error> {
1803 if desc.typ.column_types.len() != desc.metadata.len() {
1804 anyhow::bail!("mismatch between number of types and metadatas");
1805 }
1806
1807 for (col_idx, meta) in &desc.metadata {
1808 if col_idx.0 > desc.metadata.len() {
1809 anyhow::bail!("column index out of bounds");
1810 }
1811 if meta.added >= meta.dropped.unwrap_or(RelationVersion(u64::MAX)) {
1812 anyhow::bail!("column was added after it was dropped?");
1813 }
1814 if desc.typ().columns().get(meta.typ_idx).is_none() {
1815 anyhow::bail!("typ_idx incorrect");
1816 }
1817 }
1818
1819 for keys in &desc.typ.keys {
1820 for key in keys {
1821 if *key >= desc.typ.column_types.len() {
1822 anyhow::bail!("key index was out of bounds!");
1823 }
1824 }
1825 }
1826
1827 let versions = desc
1828 .metadata
1829 .values()
1830 .map(|meta| meta.dropped.unwrap_or(meta.added));
1831 let mut max = 0;
1832 let mut sum = 0;
1833 for version in versions {
1834 max = std::cmp::max(max, version.0);
1835 sum += version.0;
1836 }
1837
1838 if sum != (max * (max + 1) / 2) {
1848 anyhow::bail!("there is a duplicate or missing relation version");
1849 }
1850
1851 Ok(())
1852 }
1853
1854 assert_ok!(validate_inner(&self.inner), "validate failed! {self:?}");
1855 }
1856}
1857
1858#[derive(Debug)]
1861#[cfg(any(test, feature = "proptest"))]
1862pub enum PropRelationDescDiff {
1863 AddColumn {
1864 name: ColumnName,
1865 typ: SqlColumnType,
1866 },
1867 DropColumn {
1868 name: ColumnName,
1869 },
1870 ToggleNullability {
1871 name: ColumnName,
1872 },
1873 ChangeType {
1874 name: ColumnName,
1875 typ: SqlColumnType,
1876 },
1877}
1878
1879#[cfg(any(test, feature = "proptest"))]
1880impl PropRelationDescDiff {
1881 pub fn apply(self, desc: &mut RelationDesc) {
1882 match self {
1883 PropRelationDescDiff::AddColumn { name, typ } => {
1884 let new_idx = desc.metadata.len();
1885 let meta = ColumnMetadata {
1886 name,
1887 typ_idx: new_idx,
1888 added: RelationVersion(0),
1889 dropped: None,
1890 };
1891 let prev = desc.metadata.insert(ColumnIndex(new_idx), meta);
1892 desc.typ.column_types.push(typ);
1893
1894 assert_none!(prev);
1895 assert_eq!(desc.metadata.len(), desc.typ.column_types.len());
1896 }
1897 PropRelationDescDiff::DropColumn { name } => {
1898 let next_version = desc
1899 .metadata
1900 .values()
1901 .map(|meta| meta.dropped.unwrap_or(meta.added))
1902 .max()
1903 .unwrap_or_else(RelationVersion::root)
1904 .bump();
1905 let Some(metadata) = desc.metadata.values_mut().find(|meta| meta.name == name)
1906 else {
1907 return;
1908 };
1909 if metadata.dropped.is_none() {
1910 metadata.dropped = Some(next_version);
1911 }
1912 }
1913 PropRelationDescDiff::ToggleNullability { name } => {
1914 let Some((pos, _)) = desc.get_by_name(&name) else {
1915 return;
1916 };
1917 let col_type = desc
1918 .typ
1919 .column_types
1920 .get_mut(pos)
1921 .expect("ColumnNames and SqlColumnTypes out of sync!");
1922 col_type.nullable = !col_type.nullable;
1923 }
1924 PropRelationDescDiff::ChangeType { name, typ } => {
1925 let Some((pos, _)) = desc.get_by_name(&name) else {
1926 return;
1927 };
1928 let col_type = desc
1929 .typ
1930 .column_types
1931 .get_mut(pos)
1932 .expect("ColumnNames and SqlColumnTypes out of sync!");
1933 *col_type = typ;
1934 }
1935 }
1936 }
1937}
1938
1939#[cfg(any(test, feature = "proptest"))]
1941pub fn arb_relation_desc_diff(
1942 source: &RelationDesc,
1943) -> impl Strategy<Value = Vec<PropRelationDescDiff>> + use<> {
1944 let source = Rc::new(source.clone());
1945 let num_source_columns = source.typ.columns().len();
1946
1947 let num_add_columns = Union::new_weighted(vec![(100, Just(0..8)), (1, Just(8..64))]);
1948 let add_columns_strat = num_add_columns
1949 .prop_flat_map(|num_columns| {
1950 proptest::collection::vec((any::<ColumnName>(), any::<SqlColumnType>()), num_columns)
1951 })
1952 .prop_map(|cols| {
1953 cols.into_iter()
1954 .map(|(name, typ)| PropRelationDescDiff::AddColumn { name, typ })
1955 .collect::<Vec<_>>()
1956 });
1957
1958 if num_source_columns == 0 {
1960 return add_columns_strat.boxed();
1961 }
1962
1963 let source_ = Rc::clone(&source);
1964 let drop_columns_strat = (0..num_source_columns).prop_perturb(move |num_columns, mut rng| {
1965 let mut set = BTreeSet::default();
1966 for _ in 0..num_columns {
1967 let col_idx = rng.random_range(0..num_source_columns);
1968 set.insert(source_.get_name(col_idx).clone());
1969 }
1970 set.into_iter()
1971 .map(|name| PropRelationDescDiff::DropColumn { name })
1972 .collect::<Vec<_>>()
1973 });
1974
1975 let source_ = Rc::clone(&source);
1976 let toggle_nullability_strat =
1977 (0..num_source_columns).prop_perturb(move |num_columns, mut rng| {
1978 let mut set = BTreeSet::default();
1979 for _ in 0..num_columns {
1980 let col_idx = rng.random_range(0..num_source_columns);
1981 set.insert(source_.get_name(col_idx).clone());
1982 }
1983 set.into_iter()
1984 .map(|name| PropRelationDescDiff::ToggleNullability { name })
1985 .collect::<Vec<_>>()
1986 });
1987
1988 let source_ = Rc::clone(&source);
1989 let change_type_strat = (0..num_source_columns)
1990 .prop_perturb(move |num_columns, mut rng| {
1991 let mut set = BTreeSet::default();
1992 for _ in 0..num_columns {
1993 let col_idx = rng.random_range(0..num_source_columns);
1994 set.insert(source_.get_name(col_idx).clone());
1995 }
1996 set
1997 })
1998 .prop_flat_map(|cols| {
1999 proptest::collection::vec(any::<SqlColumnType>(), cols.len())
2000 .prop_map(move |types| (cols.clone(), types))
2001 })
2002 .prop_map(|(cols, types)| {
2003 cols.into_iter()
2004 .zip_eq(types)
2005 .map(|(name, typ)| PropRelationDescDiff::ChangeType { name, typ })
2006 .collect::<Vec<_>>()
2007 });
2008
2009 (
2010 add_columns_strat,
2011 drop_columns_strat,
2012 toggle_nullability_strat,
2013 change_type_strat,
2014 )
2015 .prop_map(|(adds, drops, toggles, changes)| {
2016 adds.into_iter()
2017 .chain(drops)
2018 .chain(toggles)
2019 .chain(changes)
2020 .collect::<Vec<_>>()
2021 })
2022 .prop_shuffle()
2023 .boxed()
2024}
2025
2026#[cfg(test)]
2027mod tests {
2028 use super::*;
2029 use prost::Message;
2030
2031 #[mz_ore::test]
2032 #[cfg_attr(miri, ignore)] fn smoktest_at_version() {
2034 let desc = RelationDesc::builder()
2035 .with_column("a", SqlScalarType::Bool.nullable(true))
2036 .with_column("z", SqlScalarType::String.nullable(false))
2037 .finish();
2038
2039 let mut versioned_desc = VersionedRelationDesc {
2040 inner: desc.clone(),
2041 };
2042 versioned_desc.validate();
2043
2044 let latest = versioned_desc.at_version(RelationVersionSelector::Latest);
2045 assert_eq!(desc, latest);
2046
2047 let v0 = versioned_desc.at_version(RelationVersionSelector::specific(0));
2048 assert_eq!(desc, v0);
2049
2050 let v3 = versioned_desc.at_version(RelationVersionSelector::specific(3));
2051 assert_eq!(desc, v3);
2052
2053 let v1 = versioned_desc.add_column("b", SqlScalarType::Bytes.nullable(false));
2054 assert_eq!(v1, RelationVersion(1));
2055
2056 let v1 = versioned_desc.at_version(RelationVersionSelector::Specific(v1));
2057 insta::assert_json_snapshot!(v1.metadata, @r###"
2058 {
2059 "0": {
2060 "name": "a",
2061 "typ_idx": 0,
2062 "added": 0,
2063 "dropped": null
2064 },
2065 "1": {
2066 "name": "z",
2067 "typ_idx": 1,
2068 "added": 0,
2069 "dropped": null
2070 },
2071 "2": {
2072 "name": "b",
2073 "typ_idx": 2,
2074 "added": 1,
2075 "dropped": null
2076 }
2077 }
2078 "###);
2079
2080 let v0_b = versioned_desc.at_version(RelationVersionSelector::specific(0));
2082 assert!(v0.iter().eq(v0_b.iter()));
2083
2084 let v2 = versioned_desc.drop_column("z");
2085 assert_eq!(v2, RelationVersion(2));
2086
2087 let v2 = versioned_desc.at_version(RelationVersionSelector::Specific(v2));
2088 insta::assert_json_snapshot!(v2.metadata, @r###"
2089 {
2090 "0": {
2091 "name": "a",
2092 "typ_idx": 0,
2093 "added": 0,
2094 "dropped": null
2095 },
2096 "2": {
2097 "name": "b",
2098 "typ_idx": 1,
2099 "added": 1,
2100 "dropped": null
2101 }
2102 }
2103 "###);
2104
2105 let v0_c = versioned_desc.at_version(RelationVersionSelector::specific(0));
2107 assert!(v0.iter().eq(v0_c.iter()));
2108
2109 let v1_b = versioned_desc.at_version(RelationVersionSelector::specific(1));
2110 assert!(v1.iter().eq(v1_b.iter()));
2111
2112 insta::assert_json_snapshot!(versioned_desc.inner.metadata, @r###"
2113 {
2114 "0": {
2115 "name": "a",
2116 "typ_idx": 0,
2117 "added": 0,
2118 "dropped": null
2119 },
2120 "1": {
2121 "name": "z",
2122 "typ_idx": 1,
2123 "added": 0,
2124 "dropped": 2
2125 },
2126 "2": {
2127 "name": "b",
2128 "typ_idx": 2,
2129 "added": 1,
2130 "dropped": null
2131 }
2132 }
2133 "###);
2134 }
2135
2136 #[mz_ore::test]
2137 #[cfg_attr(miri, ignore)] fn test_dropping_columns_with_keys() {
2139 let desc = RelationDesc::builder()
2140 .with_column("a", SqlScalarType::Bool.nullable(true))
2141 .with_column("z", SqlScalarType::String.nullable(false))
2142 .with_key(vec![1])
2143 .finish();
2144
2145 let mut versioned_desc = VersionedRelationDesc {
2146 inner: desc.clone(),
2147 };
2148 versioned_desc.validate();
2149
2150 let v1 = versioned_desc.drop_column("a");
2151 assert_eq!(v1, RelationVersion(1));
2152
2153 let v1 = versioned_desc.at_version(RelationVersionSelector::Specific(v1));
2155 insta::assert_json_snapshot!(v1, @r###"
2156 {
2157 "typ": {
2158 "column_types": [
2159 {
2160 "scalar_type": "String",
2161 "nullable": false
2162 }
2163 ],
2164 "keys": [
2165 [
2166 0
2167 ]
2168 ]
2169 },
2170 "metadata": {
2171 "1": {
2172 "name": "z",
2173 "typ_idx": 0,
2174 "added": 0,
2175 "dropped": null
2176 }
2177 }
2178 }
2179 "###);
2180
2181 let v0 = versioned_desc.at_version(RelationVersionSelector::specific(0));
2183 insta::assert_json_snapshot!(v0, @r###"
2184 {
2185 "typ": {
2186 "column_types": [
2187 {
2188 "scalar_type": "Bool",
2189 "nullable": true
2190 },
2191 {
2192 "scalar_type": "String",
2193 "nullable": false
2194 }
2195 ],
2196 "keys": [
2197 [
2198 1
2199 ]
2200 ]
2201 },
2202 "metadata": {
2203 "0": {
2204 "name": "a",
2205 "typ_idx": 0,
2206 "added": 0,
2207 "dropped": 1
2208 },
2209 "1": {
2210 "name": "z",
2211 "typ_idx": 1,
2212 "added": 0,
2213 "dropped": null
2214 }
2215 }
2216 }
2217 "###);
2218 }
2219
2220 #[mz_ore::test]
2221 #[cfg_attr(miri, ignore)] fn roundtrip_relation_desc_without_metadata() {
2223 let typ = ProtoRelationType {
2224 column_types: vec![
2225 SqlScalarType::String.nullable(false).into_proto(),
2226 SqlScalarType::Bool.nullable(true).into_proto(),
2227 ],
2228 keys: vec![],
2229 };
2230 let proto = ProtoRelationDesc {
2231 typ: Some(typ),
2232 names: vec![
2233 ColumnName("a".into()).into_proto(),
2234 ColumnName("b".into()).into_proto(),
2235 ],
2236 metadata: vec![],
2237 };
2238 let desc: RelationDesc = proto.into_rust().unwrap();
2239
2240 insta::assert_json_snapshot!(desc, @r###"
2241 {
2242 "typ": {
2243 "column_types": [
2244 {
2245 "scalar_type": "String",
2246 "nullable": false
2247 },
2248 {
2249 "scalar_type": "Bool",
2250 "nullable": true
2251 }
2252 ],
2253 "keys": []
2254 },
2255 "metadata": {
2256 "0": {
2257 "name": "a",
2258 "typ_idx": 0,
2259 "added": 0,
2260 "dropped": null
2261 },
2262 "1": {
2263 "name": "b",
2264 "typ_idx": 1,
2265 "added": 0,
2266 "dropped": null
2267 }
2268 }
2269 }
2270 "###);
2271 }
2272
2273 #[mz_ore::test]
2274 #[should_panic(expected = "column named 'a' already exists!")]
2275 fn test_add_column_with_same_name_panics() {
2276 let desc = RelationDesc::builder()
2277 .with_column("a", SqlScalarType::Bool.nullable(true))
2278 .finish();
2279 let mut versioned = VersionedRelationDesc::new(desc);
2280
2281 let _ = versioned.add_column("a", SqlScalarType::String.nullable(false));
2282 }
2283
2284 #[mz_ore::test]
2285 #[cfg_attr(miri, ignore)] fn test_add_column_with_same_name_prev_dropped() {
2287 let desc = RelationDesc::builder()
2288 .with_column("a", SqlScalarType::Bool.nullable(true))
2289 .finish();
2290 let mut versioned = VersionedRelationDesc::new(desc);
2291
2292 let v1 = versioned.drop_column("a");
2293 let v1 = versioned.at_version(RelationVersionSelector::Specific(v1));
2294 insta::assert_json_snapshot!(v1, @r###"
2295 {
2296 "typ": {
2297 "column_types": [],
2298 "keys": []
2299 },
2300 "metadata": {}
2301 }
2302 "###);
2303
2304 let v2 = versioned.add_column("a", SqlScalarType::String.nullable(false));
2305 let v2 = versioned.at_version(RelationVersionSelector::Specific(v2));
2306 insta::assert_json_snapshot!(v2, @r###"
2307 {
2308 "typ": {
2309 "column_types": [
2310 {
2311 "scalar_type": "String",
2312 "nullable": false
2313 }
2314 ],
2315 "keys": []
2316 },
2317 "metadata": {
2318 "1": {
2319 "name": "a",
2320 "typ_idx": 0,
2321 "added": 2,
2322 "dropped": null
2323 }
2324 }
2325 }
2326 "###);
2327 }
2328
2329 #[mz_ore::test]
2330 #[cfg_attr(miri, ignore)]
2331 fn apply_demand() {
2332 let desc = RelationDesc::builder()
2333 .with_column("a", SqlScalarType::String.nullable(true))
2334 .with_column("b", SqlScalarType::Int64.nullable(false))
2335 .with_column("c", SqlScalarType::Time.nullable(false))
2336 .finish();
2337 let desc = desc.apply_demand(&BTreeSet::from([0, 2]));
2338 assert_eq!(desc.arity(), 2);
2339 VersionedRelationDesc::new(desc).validate();
2341 }
2342
2343 #[mz_ore::test]
2344 #[cfg_attr(miri, ignore)]
2345 fn smoketest_column_index_stable_ident() {
2346 let idx_a = ColumnIndex(42);
2347 assert_eq!(idx_a.to_stable_name(), "42");
2349 }
2350
2351 #[mz_ore::test]
2352 #[cfg_attr(miri, ignore)] fn proptest_relation_desc_roundtrips() {
2354 fn testcase(og: RelationDesc) {
2355 let bytes = og.into_proto().encode_to_vec();
2356 let proto = ProtoRelationDesc::decode(&bytes[..]).unwrap();
2357 let rnd = RelationDesc::from_proto(proto).unwrap();
2358
2359 assert_eq!(og, rnd);
2360 }
2361
2362 proptest!(|(desc in any::<RelationDesc>())| {
2363 testcase(desc);
2364 });
2365
2366 let strat = any::<RelationDesc>().prop_flat_map(|desc| {
2367 arb_relation_desc_diff(&desc).prop_map(move |diffs| (desc.clone(), diffs))
2368 });
2369
2370 proptest!(|((mut desc, diffs) in strat)| {
2371 for diff in diffs {
2372 diff.apply(&mut desc);
2373 };
2374 testcase(desc);
2375 });
2376 }
2377}