1use super::{Arc, ColumnType, HashMap, RowSchema};
10
11pub type RecordFields = Arc<[Option<ColumnType>]>;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub(crate) struct RecordMetadata {
17 pub types: Option<RecordFields>,
18 pub descriptor: Option<Arc<RecordDescriptor>>,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub(crate) struct RecordFieldMetadata {
23 pub collation_oid: u32,
24 pub sql_unknown: bool,
25}
26
27#[derive(Debug, PartialEq, Eq)]
29pub(crate) struct RecordDescriptor {
30 pub types: RecordFields,
31 pub names: Option<Box<[String]>>,
32 pub fields: Box<[RecordFieldMetadata]>,
33}
34
35impl RecordDescriptor {
36 pub fn position(&self, name: &str) -> Option<usize> {
37 self.names.as_ref().map_or_else(
38 || {
39 crate::type_resolution::field_selection::anonymous_field_position(
40 name,
41 self.types.len(),
42 )
43 },
44 |names| names.iter().position(|candidate| candidate == name),
45 )
46 }
47}
48
49impl RowSchema {
50 pub fn record_fields(&self, logical: usize) -> Option<&RecordFields> {
51 self.slot(logical)
52 .and_then(|slot| self.physical_record_fields(slot))
53 }
54
55 pub fn physical_record_fields(&self, slot: usize) -> Option<&RecordFields> {
56 self.physical_record_metadata(slot)?.types.as_ref()
57 }
58
59 pub(crate) fn physical_record_metadata(&self, slot: usize) -> Option<&RecordMetadata> {
60 self.index.cold.record_fields.get(&slot)
61 }
62
63 pub(crate) fn record_metadata(&self, logical: usize) -> Option<&RecordMetadata> {
64 self.slot(logical)
65 .and_then(|slot| self.index.cold.record_fields.get(&slot))
66 }
67
68 pub(crate) fn input_record_metadata(
69 &self,
70 expression: &crate::ScalarExpr,
71 ) -> Option<&RecordMetadata> {
72 let slot = match expression {
73 crate::ScalarExpr::Column(name) => self.column_slot(name),
74 crate::ScalarExpr::QualifiedColumn { qualifier, column } => {
75 self.qualified_slot(qualifier, column)
76 }
77 crate::ScalarExpr::InternalColumn(column) => self.internal_slot(*column),
78 crate::ScalarExpr::Position(position) => self.slot(*position),
79 _ => None,
80 }?;
81 self.index.cold.record_fields.get(&slot)
82 }
83
84 pub fn with_record_fields(
86 self,
87 fields: impl IntoIterator<Item = (usize, RecordFields)>,
88 ) -> Self {
89 self.with_record_metadata(fields.into_iter().map(|(position, types)| {
90 (
91 position,
92 RecordMetadata {
93 types: Some(types),
94 descriptor: None,
95 },
96 )
97 }))
98 }
99
100 pub(crate) fn with_record_metadata(
101 mut self,
102 fields: impl IntoIterator<Item = (usize, RecordMetadata)>,
103 ) -> Self {
104 for (logical, fields) in fields {
105 if let Some(slot) = self.slot(logical) {
106 Arc::make_mut(&mut self.index)
107 .cold
108 .record_fields
109 .insert(slot, fields);
110 }
111 }
112 self
113 }
114
115 pub fn with_record_fields_from(self, input: &Self) -> Self {
117 let width = self.len().min(input.len());
118 self.with_record_metadata((0..width).filter_map(|index| {
119 input
120 .record_metadata(index)
121 .cloned()
122 .map(|fields| (index, fields))
123 }))
124 }
125
126 pub(super) fn joined_record_fields(
127 left: &Self,
128 right: &Self,
129 right_base: usize,
130 ) -> HashMap<usize, RecordMetadata> {
131 let mut fields = left.index.cold.record_fields.clone();
132 fields.extend(
133 right
134 .index
135 .cold
136 .record_fields
137 .iter()
138 .map(|(slot, fields)| (right_base + slot, fields.clone())),
139 );
140 fields
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn projection_alias_join_and_compaction_preserve_record_field_slots() {
150 let fields: RecordFields =
151 vec![Some(ColumnType::Integer), Some(ColumnType::BigInteger)].into();
152 let descriptor = Arc::new(RecordDescriptor {
153 types: fields.clone(),
154 names: None,
155 fields: vec![
156 RecordFieldMetadata {
157 collation_oid: 0,
158 sql_unknown: false
159 };
160 2
161 ]
162 .into(),
163 });
164 let input = RowSchema::with_types(
165 vec!["discard".into(), "value".into()],
166 vec![Some(ColumnType::Text), Some(ColumnType::Record)],
167 )
168 .with_record_metadata([(
169 1,
170 RecordMetadata {
171 types: Some(fields.clone()),
172 descriptor: Some(descriptor.clone()),
173 },
174 )]);
175 let selected = RowSchema::select(&input, &[("renamed".into(), "value".into())]);
176 assert_eq!(selected.record_fields(0), Some(&fields));
177 let qualified = RowSchema::with_relation_qualifier(&selected, "r");
178 assert_eq!(
179 qualified.physical_record_fields(qualified.qualified_slot("r", "renamed").unwrap()),
180 Some(&fields)
181 );
182 let (compact, _) = qualified.canonical_projection();
183 assert_eq!(compact.record_fields(0), Some(&fields));
184 let public = qualified.for_result_columns(qualified.columns());
185 assert_eq!(public.physical_width(), 1);
186 assert_eq!(public.qualified_slot("r", "renamed"), None);
187 assert_eq!(public.position("discard"), None);
188 let outer = RowSchema::with_outer_schema(&RowSchema::default(), &compact);
189 assert_eq!(
190 outer.physical_record_fields(outer.qualified_slot("r", "renamed").unwrap()),
191 Some(&fields)
192 );
193 for schema in [&selected, &qualified, &compact, &public] {
194 assert!(Arc::ptr_eq(
195 &descriptor,
196 schema
197 .record_metadata(0)
198 .unwrap()
199 .descriptor
200 .as_ref()
201 .unwrap()
202 ));
203 assert!(Arc::ptr_eq(&fields, schema.record_fields(0).unwrap()));
204 }
205 assert!(Arc::ptr_eq(
206 &descriptor,
207 outer
208 .physical_record_metadata(outer.qualified_slot("r", "renamed").unwrap())
209 .unwrap()
210 .descriptor
211 .as_ref()
212 .unwrap()
213 ));
214 }
215}