Skip to main content

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}