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