1use super::RowSchema;
10use crate::ast::{ColumnDef, ColumnType, InternalColumnRef};
11
12pub trait ScalarTypeSchema {
14 fn observed_expression_type(&self, _: &crate::ScalarExpr) -> Option<Option<&ColumnType>> {
16 None
17 }
18
19 fn has_unqualified_column(&self, name: &str) -> bool;
20 fn column_is_ambiguous(&self, name: &str) -> bool;
21 fn type_of(&self, name: &str) -> Option<&ColumnType>;
22 fn column_type(&self, position: usize) -> Option<&ColumnType>;
23 fn internal_type(&self, column: InternalColumnRef) -> Option<&ColumnType>;
24 fn has_qualifier(&self, qualifier: &str) -> bool;
25 fn whole_row_type(&self, _qualifier: &str) -> Option<&ColumnType> {
27 None
28 }
29 fn has_qualified_column(&self, qualifier: &str, name: &str) -> bool;
30 fn qualified_column_is_ambiguous(&self, qualifier: &str, name: &str) -> bool;
31 fn qualified_type(&self, qualifier: &str, name: &str) -> Option<&ColumnType>;
32 fn columns_are_open(&self, qualifier: Option<&str>) -> bool;
33
34 fn column_collation(&self, position: usize) -> Option<u32> {
35 self.column_type(position).map(default_collation)
36 }
37 fn collation_of(&self, name: &str) -> Option<u32> {
38 self.type_of(name).map(default_collation)
39 }
40 fn qualified_collation(&self, qualifier: &str, name: &str) -> Option<u32> {
41 self.qualified_type(qualifier, name).map(default_collation)
42 }
43 fn internal_collation(&self, column: InternalColumnRef) -> Option<u32> {
44 self.internal_type(column).map(default_collation)
45 }
46
47 fn physical_schema(&self) -> Option<&RowSchema>;
49}
50
51fn default_collation(ty: &ColumnType) -> u32 {
52 u32::try_from(crate::catalog::type_metadata::pg_type_collation_oid(ty))
53 .expect("type collation identity")
54}
55
56impl ScalarTypeSchema for RowSchema {
57 fn column_collation(&self, position: usize) -> Option<u32> {
58 Self::column_collation(self, position)
59 }
60 fn collation_of(&self, name: &str) -> Option<u32> {
61 Self::collation_of(self, name)
62 }
63 fn qualified_collation(&self, qualifier: &str, name: &str) -> Option<u32> {
64 Self::qualified_collation(self, qualifier, name)
65 }
66 fn internal_collation(&self, column: InternalColumnRef) -> Option<u32> {
67 Self::internal_collation(self, column)
68 }
69
70 fn has_unqualified_column(&self, name: &str) -> bool {
71 Self::has_unqualified_column(self, name)
72 }
73 fn column_is_ambiguous(&self, name: &str) -> bool {
74 Self::column_is_ambiguous(self, name)
75 }
76 fn type_of(&self, name: &str) -> Option<&ColumnType> {
77 Self::type_of(self, name)
78 }
79 fn column_type(&self, position: usize) -> Option<&ColumnType> {
80 Self::column_type(self, position)
81 }
82 fn internal_type(&self, column: InternalColumnRef) -> Option<&ColumnType> {
83 Self::internal_type(self, column)
84 }
85 fn has_qualifier(&self, qualifier: &str) -> bool {
86 Self::has_qualifier(self, qualifier)
87 }
88 fn whole_row_type(&self, qualifier: &str) -> Option<&ColumnType> {
89 self.index.cold.row_types.get(qualifier)
90 }
91 fn has_qualified_column(&self, qualifier: &str, name: &str) -> bool {
92 Self::has_qualified_column(self, qualifier, name)
93 }
94 fn qualified_column_is_ambiguous(&self, qualifier: &str, name: &str) -> bool {
95 Self::qualified_column_is_ambiguous(self, qualifier, name)
96 }
97 fn qualified_type(&self, qualifier: &str, name: &str) -> Option<&ColumnType> {
98 Self::qualified_type(self, qualifier, name)
99 }
100 fn columns_are_open(&self, qualifier: Option<&str>) -> bool {
101 Self::columns_are_open(self, qualifier)
102 }
103 fn physical_schema(&self) -> Option<&RowSchema> {
104 Some(self)
105 }
106}
107
108#[derive(Clone, Copy)]
110pub struct ColumnTypeSchema<'a> {
111 columns: &'a [ColumnDef],
112 relation: Option<(&'a str, &'a ColumnType)>,
113}
114
115impl<'a> ColumnTypeSchema<'a> {
116 pub fn new(columns: &'a [ColumnDef]) -> Self {
117 Self {
118 columns,
119 relation: None,
120 }
121 }
122
123 pub fn with_relation(
125 columns: &'a [ColumnDef],
126 name: &'a str,
127 row_type: &'a ColumnType,
128 ) -> Result<Self, crate::SQLError> {
129 if !matches!(row_type, ColumnType::Composite(_)) {
130 return Err(crate::SQLError::Internal(
131 "a relation scope requires its named composite row type".into(),
132 ));
133 }
134 Ok(Self {
135 columns,
136 relation: Some((name, row_type)),
137 })
138 }
139
140 pub(crate) fn system_column_type(&self, name: &str) -> Option<&ColumnType> {
141 (self.relation.is_some() && name == "tableoid").then_some(&ColumnType::Oid)
142 }
143
144 pub fn columns(&self) -> &'a [ColumnDef] {
146 self.columns
147 }
148}
149
150impl ScalarTypeSchema for ColumnTypeSchema<'_> {
151 fn column_collation(&self, position: usize) -> Option<u32> {
152 self.columns.get(position).map(|column| {
153 column
154 .collation_oid
155 .unwrap_or_else(|| default_collation(&column.ty))
156 })
157 }
158 fn collation_of(&self, name: &str) -> Option<u32> {
159 let mut matching = self
160 .columns
161 .iter()
162 .enumerate()
163 .filter(|(_, column)| column.name == name);
164 let Some((position, _)) = matching.next() else {
165 return self.system_column_type(name).map(default_collation);
166 };
167 matching
168 .next()
169 .is_none()
170 .then(|| self.column_collation(position))
171 .flatten()
172 }
173
174 fn has_unqualified_column(&self, name: &str) -> bool {
175 self.columns.iter().any(|column| column.name == name)
176 || self.system_column_type(name).is_some()
177 }
178 fn column_is_ambiguous(&self, name: &str) -> bool {
179 self.columns
180 .iter()
181 .filter(|column| column.name == name)
182 .nth(1)
183 .is_some()
184 }
185 fn type_of(&self, name: &str) -> Option<&ColumnType> {
186 let mut matching = self.columns.iter().filter(|column| column.name == name);
187 let Some(first) = matching.next() else {
188 return self.system_column_type(name);
189 };
190 matching.next().is_none().then_some(&first.ty)
191 }
192 fn column_type(&self, position: usize) -> Option<&ColumnType> {
193 self.columns.get(position).map(|column| &column.ty)
194 }
195 fn internal_type(&self, _: InternalColumnRef) -> Option<&ColumnType> {
196 None
197 }
198 fn has_qualifier(&self, qualifier: &str) -> bool {
199 self.whole_row_type(qualifier).is_some()
200 }
201 fn whole_row_type(&self, qualifier: &str) -> Option<&ColumnType> {
202 let (name, row_type) = self.relation?;
203 let ColumnType::Composite(reference) = row_type else {
204 unreachable!("relation scope validates its row type");
205 };
206 (qualifier == name || qualifier == reference.name).then_some(row_type)
207 }
208 fn has_qualified_column(&self, qualifier: &str, name: &str) -> bool {
209 self.has_qualifier(qualifier) && self.has_unqualified_column(name)
210 }
211 fn qualified_column_is_ambiguous(&self, qualifier: &str, name: &str) -> bool {
212 self.has_qualifier(qualifier) && self.column_is_ambiguous(name)
213 }
214 fn qualified_type(&self, qualifier: &str, name: &str) -> Option<&ColumnType> {
215 self.has_qualifier(qualifier)
216 .then(|| self.type_of(name))
217 .flatten()
218 }
219 fn qualified_collation(&self, qualifier: &str, name: &str) -> Option<u32> {
220 self.has_qualifier(qualifier)
221 .then(|| self.collation_of(name))
222 .flatten()
223 }
224 fn columns_are_open(&self, _: Option<&str>) -> bool {
225 false
226 }
227 fn physical_schema(&self) -> Option<&RowSchema> {
228 None
229 }
230}
231
232#[cfg(test)]
233mod tests;