inillucent_sql/bind/column_names.rs
1//! The names the columns of a derived table, a view and a `CREATE TABLE ... AS`
2//! have.
3//!
4//! Invariant: **no two columns of one of them have the same name, ignoring
5//! ASCII case, and the rule that makes them different is SQLite's.** The second
6//! `a` of `SELECT a, a FROM t` becomes `a:1`, the third `a:2`. A name that
7//! already ends in `:` and digits has them replaced rather than extended, so a
8//! collision with `a:1` gives `a:2` and never `a:1:1`. A column with no name,
9//! or whose name is `true` or `false`, is called `column` and its position,
10//! counting from one. The top-level result of a `SELECT` is not renamed: its
11//! columns keep their duplicates, which is what a caller reading the header of
12//! `SELECT a, a FROM t` sees in SQLite too.
13
14use super::BoundSelect;
15use crate::catalog_view::ColumnInfo;
16
17/// Checks a view's declared column list against its body, and names the
18/// view's columns by the declarations they resolve to.
19///
20/// **The width is checked when the view is read, not when it is created.**
21/// SQLite does it in `sqlite3ViewGetColumnNames`, at the first use, so a view
22/// declared with a column list of the wrong width is created and fails each
23/// time it is read. Only a derived table keeps the spelling that was typed for
24/// a bare column; a view uses the declaration's.
25///
26/// @param bound - the view's bound body
27/// @param columns - the column list the view was declared with, if any
28/// @param view_name - the view's name, for the message
29/// @param span - where the view was named
30pub(super) fn finish_view_columns(
31 bound: &mut BoundSelect,
32 columns: &[Vec<u8>],
33 view_name: &[u8],
34 span: crate::lexer::Span,
35) -> Result<(), crate::diagnostic::ParseError> {
36 if !columns.is_empty() && columns.len() != bound.columns.len() {
37 return Err(super::refused(
38 format!(
39 "expected {} columns for '{}' but got {}",
40 columns.len(),
41 String::from_utf8_lossy(view_name),
42 bound.columns.len()
43 ),
44 span,
45 ));
46 }
47 for column in &mut bound.columns {
48 column.written = None;
49 }
50 Ok(())
51}
52
53/// Returns the names with duplicates made unique.
54///
55/// @param names - the names in column order, as written or derived
56pub fn unique_column_names(names: &[Vec<u8>]) -> Vec<Vec<u8>> {
57 let mut taken: Vec<Vec<u8>> = Vec::with_capacity(names.len());
58 let mut out = Vec::with_capacity(names.len());
59 for (position, name) in names.iter().enumerate() {
60 let mut chosen = base_name(name, position);
61 let mut count = 0u32;
62 while taken.contains(&chosen.to_ascii_lowercase()) {
63 count = count.saturating_add(1);
64 chosen = with_suffix(&chosen, count);
65 }
66 taken.push(chosen.to_ascii_lowercase());
67 out.push(chosen);
68 }
69 out
70}
71
72/// Returns the name a column starts from, before duplicates are looked for.
73///
74/// @param name - the name as written or derived
75/// @param position - the column's position, counting from zero
76fn base_name(name: &[u8], position: usize) -> Vec<u8> {
77 // An empty name written with `AS ''` is a name, and stays empty: SQLite
78 // only invents `columnN` for a column that has no name at all.
79 let unnamed = name.eq_ignore_ascii_case(b"true") || name.eq_ignore_ascii_case(b"false");
80 if unnamed {
81 return format!("column{}", position.saturating_add(1)).into_bytes();
82 }
83 name.to_vec()
84}
85
86/// Returns a name with `:count` in place of any `:digits` it already ends in.
87///
88/// @param name - the name that collided
89/// @param count - the number to put after the colon
90fn with_suffix(name: &[u8], count: u32) -> Vec<u8> {
91 let digits = name
92 .iter()
93 .rev()
94 .take_while(|byte| byte.is_ascii_digit())
95 .count();
96 let stem_end = name.len().saturating_sub(digits);
97 let keeps_colon =
98 digits > 0 && stem_end > 0 && name.get(stem_end.saturating_sub(1)) == Some(&b':');
99 let stem = match keeps_colon {
100 true => name.get(..stem_end.saturating_sub(1)).unwrap_or(name),
101 false => name,
102 };
103 let mut out = stem.to_vec();
104 out.extend_from_slice(format!(":{count}").as_bytes());
105 out
106}
107
108/// Builds the table a nested query's rows are read through.
109///
110/// The columns are the block's result columns. Their affinity and collation
111/// come from the expressions behind them, so a comparison against a subquery
112/// column applies the rules it would have applied one level down; a column with
113/// no affinity of its own gets none, which is what SQLite does for an
114/// expression that is not a bare column or a cast.
115/// Returns the columns a nested query's result presents to a reader.
116///
117/// Public because a write to a view needs them before there is a FROM term to
118/// hang them on: the view's catalog entry carries no column list at all.
119pub fn subquery_columns(select: &BoundSelect, names: &[Vec<u8>]) -> Vec<ColumnInfo> {
120 let written: Vec<Vec<u8>> = select
121 .columns
122 .iter()
123 .enumerate()
124 .map(|(index, column)| {
125 names
126 .get(index)
127 .cloned()
128 .or_else(|| column.written.clone())
129 .unwrap_or_else(|| column.name.clone())
130 })
131 .collect();
132 let unique = unique_column_names(&written);
133 select
134 .columns
135 .iter()
136 .enumerate()
137 .zip(unique)
138 .map(|((index, column), name)| {
139 let folded = name.to_ascii_lowercase();
140 let collation = select.column_collation(index);
141 ColumnInfo {
142 name,
143 folded,
144 declared_type: column.declared_type.clone(),
145 affinity: select.column_affinity(index),
146 collation: collation.name().as_bytes().to_ascii_lowercase(),
147 not_null: false,
148 not_null_conflict: None,
149 primary_key_conflict: None,
150 default_sql: None,
151 primary_key_position: None,
152 hidden: false,
153 generated: false,
154 stored: false,
155 generated_sql: None,
156 }
157 })
158 .collect()
159}
160
161impl super::Binder<'_> {
162 /// Returns the name a bare column reference was written with, looking
163 /// through `COLLATE`.
164 ///
165 /// A derived table names such a column after the identifier as it was
166 /// typed, not after the declaration it resolved to: `SELECT * FROM (SELECT
167 /// ABC FROM t)` over `t(Abc)` has a column called `ABC`. Parentheses leave
168 /// no node in the tree, so `(abc)` is the same as `abc`.
169 ///
170 /// @param id - the result column's expression
171 pub(super) fn written_column_name(&self, id: crate::ast::ExprId) -> Option<&[u8]> {
172 let mut current = id;
173 loop {
174 match self.ast.expr(current)? {
175 crate::ast::Expr::Collate { operand, .. } => current = *operand,
176 crate::ast::Expr::Column { column, .. } => return Some(self.ast.text(*column)),
177 _ => return None,
178 }
179 }
180 }
181}
182
183impl super::Binder<'_> {
184 /// Returns a column reference as it was written, with its qualifiers.
185 ///
186 /// SQLite prints the reference the way the statement spelled it, so
187 /// `SELECT main.t.zz` fails with `no such column: main.t.zz`.
188 ///
189 /// @param database - the schema qualifier, when one was written
190 /// @param table - the table qualifier, when one was written
191 /// @param column - the column name
192 pub(super) fn reference_label(
193 &self,
194 database: Option<crate::ast::NameId>,
195 table: Option<crate::ast::NameId>,
196 column: crate::ast::NameId,
197 ) -> Vec<u8> {
198 let mut label = Vec::new();
199 for part in [database, table].into_iter().flatten() {
200 label.extend_from_slice(self.ast.text(part));
201 label.push(b'.');
202 }
203 label.extend_from_slice(self.ast.text(column));
204 label
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 fn names(list: &[&str]) -> Vec<Vec<u8>> {
213 list.iter().map(|name| name.as_bytes().to_vec()).collect()
214 }
215
216 /// Duplicates get `:1`, `:2`, and the comparison ignores case.
217 #[test]
218 fn duplicates_are_numbered_without_regard_to_case() {
219 assert_eq!(
220 unique_column_names(&names(&["a", "a", "A"])),
221 names(&["a", "a:1", "A:2"])
222 );
223 }
224
225 /// An existing `:digits` suffix is replaced, not extended.
226 #[test]
227 fn an_existing_suffix_is_replaced() {
228 assert_eq!(
229 unique_column_names(&names(&["x:1", "x", "x"])),
230 names(&["x:1", "x", "x:2"])
231 );
232 }
233
234 /// An empty name, `true` and `false` become `column` and a position.
235 #[test]
236 fn unnamed_columns_are_called_column_and_a_position() {
237 assert_eq!(
238 unique_column_names(&names(&["", "TRUE", "b"])),
239 names(&["", "column2", "b"])
240 );
241 }
242}