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 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}