Skip to main content

mz_sql_parser/ast/defs/
name.rs

1// Copyright 2018 sqlparser-rs contributors. All rights reserved.
2// Copyright Materialize, Inc. and contributors. All rights reserved.
3//
4// This file is derived from the sqlparser-rs project, available at
5// https://github.com/andygrove/sqlparser-rs. It was incorporated
6// directly into Materialize on December 21, 2019.
7//
8// Licensed under the Apache License, Version 2.0 (the "License");
9// you may not use this file except in compliance with the License.
10// You may obtain a copy of the License in the LICENSE file at the
11// root of this repository, or online at
12//
13//     http://www.apache.org/licenses/LICENSE-2.0
14//
15// Unless required by applicable law or agreed to in writing, software
16// distributed under the License is distributed on an "AS IS" BASIS,
17// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18// See the License for the specific language governing permissions and
19// limitations under the License.
20
21use mz_ore::str::StrExt;
22use mz_sql_lexer::keywords::{
23    ALL, ANY, AS, DISTINCT, IF, INTO, Keyword, LIST, PREPARE, SOME, WHEN,
24};
25use mz_sql_lexer::lexer::{IdentString, MAX_IDENTIFIER_LENGTH};
26use serde::{Deserialize, Serialize};
27use std::fmt;
28
29use crate::ast::display::{self, AstDisplay, AstFormatter};
30use crate::ast::{AstInfo, QualifiedReplica};
31
32/// An identifier.
33#[derive(
34    Debug,
35    Clone,
36    PartialEq,
37    Eq,
38    Hash,
39    PartialOrd,
40    Ord,
41    Serialize,
42    Deserialize
43)]
44pub struct Ident(pub(crate) String);
45
46impl Ident {
47    /// Maximum length of an identifier in Materialize.
48    pub const MAX_LENGTH: usize = MAX_IDENTIFIER_LENGTH;
49
50    /// Create a new [`Ident`] with the given value, checking our invariants.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use mz_sql_parser::ast::Ident;
56    ///
57    /// let id = Ident::new("hello_world").unwrap();
58    /// assert_eq!(id.as_str(), "hello_world");
59    ///
60    /// let too_long = "I am a very long identifier that is more than 255 bytes long which is the max length for idents.\
61    /// 😊😁😅😂😬🍻😮‍💨😮🗽🛰️🌈😊😁😅😂😬🍻😮‍💨😮🗽🛰️🌈😊😁😅😂😬🍻😮‍💨😮🗽🛰️🌈";
62    /// assert_eq!(too_long.len(), 258);
63    ///
64    /// let too_long_id = Ident::new(too_long);
65    /// assert!(too_long_id.is_err());
66    ///
67    /// let invalid_name_dot = Ident::new(".");
68    /// assert!(invalid_name_dot.is_err());
69    ///
70    /// let invalid_name_dot_dot = Ident::new("..");
71    /// assert!(invalid_name_dot_dot.is_err());
72    /// ```
73    ///
74    pub fn new<S>(s: S) -> Result<Self, IdentError>
75    where
76        S: TryInto<IdentString>,
77        <S as TryInto<IdentString>>::Error: fmt::Display,
78    {
79        let s = s
80            .try_into()
81            .map_err(|e| IdentError::TooLong(e.to_string()))?;
82
83        if &*s == "." || &*s == ".." {
84            return Err(IdentError::Invalid(s.into_inner()));
85        }
86
87        Ok(Ident(s.into_inner()))
88    }
89
90    /// Create a new [`Ident`] modifying the given value as necessary to meet our invariants.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use mz_sql_parser::ast::Ident;
96    ///
97    /// let too_long = "🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
98    /// 🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵\
99    /// 🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴";
100    ///
101    /// let id = Ident::new_lossy(too_long);
102    ///
103    /// // `new_lossy` will truncate the provided string, since it's too long. Note the missing
104    /// // `🔴` characters.
105    /// assert_eq!(id.as_str(), "🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵");
106    /// ```
107    pub fn new_lossy<S: Into<String>>(value: S) -> Self {
108        let s: String = value.into();
109        if s.len() <= Self::MAX_LENGTH {
110            return Ident(s);
111        }
112
113        let mut byte_length = 0;
114        let s_truncated = s
115            .chars()
116            .take_while(|c| {
117                byte_length += c.len_utf8();
118                byte_length <= Self::MAX_LENGTH
119            })
120            .collect();
121
122        Ident(s_truncated)
123    }
124
125    /// Create a new [`Ident`] _without checking any of our invariants_.
126    ///
127    /// NOTE: Generally you __should not use this function__! If you're trying to create an
128    /// [`Ident`] from a `&'static str` you know is valid, use the [`ident!`] macro. For all other
129    /// use cases, see [`Ident::new`] which correctly checks our invariants.
130    ///
131    /// [`ident!`]: [`mz_sql_parser::ident`]
132    pub fn new_unchecked<S: Into<String>>(value: S) -> Self {
133        let s = value.into();
134        mz_ore::soft_assert_no_log!(s.len() <= Self::MAX_LENGTH);
135
136        Ident(s)
137    }
138
139    /// Generate a valid [`Ident`] with the provided `prefix` and `suffix`.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use mz_sql_parser::ast::{Ident, IdentError};
145    ///
146    /// let good_id =
147    ///   Ident::try_generate_name("hello", "_world", |_| Ok::<_, IdentError>(true)).unwrap();
148    /// assert_eq!(good_id.as_str(), "hello_world");
149    ///
150    /// // Return invalid once.
151    /// let mut attempts = 0;
152    /// let one_failure = Ident::try_generate_name("hello", "_world", |_candidate| {
153    ///     if attempts == 0 {
154    ///         attempts += 1;
155    ///         Ok::<_, IdentError>(false)
156    ///     } else {
157    ///         Ok(true)
158    ///     }
159    /// })
160    /// .unwrap();
161    ///
162    /// // We "hello_world" was invalid, so we appended "_1".
163    /// assert_eq!(one_failure.as_str(), "hello_world_1");
164    /// ```
165    pub fn try_generate_name<P, S, F, E>(prefix: P, suffix: S, mut is_valid: F) -> Result<Self, E>
166    where
167        P: Into<String>,
168        S: Into<String>,
169        E: From<IdentError>,
170        F: FnMut(&Ident) -> Result<bool, E>,
171    {
172        const MAX_ATTEMPTS: usize = 1000;
173
174        let prefix: String = prefix.into();
175        let suffix: String = suffix.into();
176
177        // First just append the prefix and suffix.
178        let mut candidate = Ident(prefix.clone());
179        candidate.append_lossy(suffix.clone());
180        if is_valid(&candidate)? {
181            return Ok(candidate);
182        }
183
184        // Otherwise, append a number to the back.
185        for i in 1..MAX_ATTEMPTS {
186            let mut candidate = Ident(prefix.clone());
187            candidate.append_lossy(format!("{suffix}_{i}"));
188
189            if is_valid(&candidate)? {
190                return Ok(candidate);
191            }
192        }
193
194        // Couldn't find any valid name!
195        Err(E::from(IdentError::FailedToGenerate {
196            prefix,
197            suffix,
198            attempts: MAX_ATTEMPTS,
199        }))
200    }
201
202    /// Append the provided `suffix`, truncating `self` as necessary to satisfy our invariants.
203    ///
204    /// Note: We soft-assert that the provided `suffix` is not too long, if it is, we'll
205    /// truncate it.
206    ///
207    /// # Examples
208    ///
209    /// ```
210    /// use mz_sql_parser::{
211    ///     ident,
212    ///     ast::Ident,
213    /// };
214    ///
215    /// let mut id = ident!("🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵");
216    /// id.append_lossy("🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴");
217    ///
218    /// // We truncated the original ident, removing all '🔵' chars.
219    /// assert_eq!(id.as_str(), "🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴");
220    /// ```
221    ///
222    /// ### Too long suffix
223    /// If the provided suffix is too long, we'll also truncate that.
224    ///
225    /// ```
226    /// # mz_ore::assert::SOFT_ASSERTIONS.store(false, std::sync::atomic::Ordering::Relaxed);
227    /// use mz_sql_parser::{
228    ///     ident,
229    ///     ast::Ident,
230    /// };
231    ///
232    /// let mut stem = ident!("hello___world");
233    ///
234    /// let too_long_suffix = "\
235    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
236    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
237    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
238    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🔵🔵\
239    /// ";
240    ///
241    /// stem.append_lossy(too_long_suffix);
242    ///
243    /// // Notice the "hello___world" stem got truncated, as did the "🔵🔵" characters from the suffix.
244    /// let result = "hello___wor\
245    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
246    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
247    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
248    /// 🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢\
249    /// ";
250    /// assert_eq!(stem.as_str(), result);
251    /// ```
252    pub fn append_lossy<S: Into<String>>(&mut self, suffix: S) {
253        // Make sure our suffix at least leaves a bit of room for the original ident.
254        const MAX_SUFFIX_LENGTH: usize = Ident::MAX_LENGTH - 8;
255
256        let mut suffix: String = suffix.into();
257        mz_ore::soft_assert_or_log!(suffix.len() <= MAX_SUFFIX_LENGTH, "suffix too long");
258
259        // Truncate the suffix as necessary.
260        if suffix.len() > MAX_SUFFIX_LENGTH {
261            let mut byte_length = 0;
262            suffix = suffix
263                .chars()
264                .take_while(|c| {
265                    byte_length += c.len_utf8();
266                    byte_length <= MAX_SUFFIX_LENGTH
267                })
268                .collect();
269        }
270
271        // Truncate ourselves as necessary.
272        let available_length = Ident::MAX_LENGTH - suffix.len();
273        if self.0.len() > available_length {
274            let mut byte_length = 0;
275            self.0 = self
276                .0
277                .chars()
278                .take_while(|c| {
279                    byte_length += c.len_utf8();
280                    byte_length <= available_length
281                })
282                .collect();
283        }
284
285        // Append the suffix.
286        self.0.push_str(&suffix);
287    }
288
289    /// Reports whether the identifier matches the regex `[a-z_][a-z0-9_]*`,
290    /// i.e. it is composed only of characters that never require quoting.
291    ///
292    /// This is the character-level half of [`Ident::can_be_printed_bare`]. It
293    /// deliberately does *not* consider keywords: whether a keyword-named
294    /// identifier needs quoting depends on the surrounding grammar (a
295    /// reparsing concern), not on its characters. Contexts that only need
296    /// legible, unambiguous output — rather than a SQL round-trip — should use
297    /// this instead (see `HumanizedExplain::humanize_ident`).
298    pub fn has_only_bare_chars(&self) -> bool {
299        let mut chars = self.0.chars();
300        chars
301            .next()
302            .map(|ch| matches!(ch, 'a'..='z' | '_'))
303            .unwrap_or(false)
304            && chars.all(|ch| matches!(ch, 'a'..='z' | '0'..='9' | '_'))
305    }
306
307    /// An identifier can be printed in bare mode if
308    ///  * it matches the regex `[a-z_][a-z0-9_]*` and
309    ///  * it is not a "reserved keyword."
310    pub fn can_be_printed_bare(&self) -> bool {
311        self.has_only_bare_chars()
312            && !self
313                .as_keyword()
314                .map(|kw| {
315                    kw.is_sometimes_reserved()
316                        || kw.begins_query_body()
317                        // `AS` at the start of a SELECT item is consumed as the
318                        // `AS OF` timestamp keyword (an empty projection), so a
319                        // bare `as` identifier/function name fails to reparse.
320                        || kw == AS
321                        // `ANY`/`ALL`/`SOME` after a comparison operator start a
322                        // quantified-comparison (`x op ANY (...)`), so a bare such
323                        // identifier — e.g. `0 # some` — reparses as the start of a
324                        // quantifier rather than an identifier.
325                        || matches!(kw, ANY | ALL | SOME)
326                        // `ALL`/`DISTINCT` right after `SELECT` are consumed as the
327                        // projection quantifier, so a bare `"all"` / `"distinct"`
328                        // column reference reparses to a quantifier with an empty
329                        // projection instead of an identifier. (`ALL` is already
330                        // covered above; quoting these keeps display-only — unlike
331                        // marking them always-reserved, which also rejects `WHERE
332                        // distinct = 1` at parse time.)
333                        || kw == DISTINCT
334                        // `LIST` followed by `[` re-lexes as a `LIST[...]` literal
335                        // (`list[1]` is a valid one-element list), so a bare `list`
336                        // identifier that gets subscripted — `"list"[1]` — would
337                        // reparse as a list literal instead of a subscript. (`ARRAY`
338                        // is reserved-in-scalar-expression and so already quoted.
339                        // `MAP` needs no clause here even though `map[…]` is a
340                        // literal too: `MAP` is a context-sensitive keyword, so
341                        // `write_subscript_receiver` parenthesizes the receiver into
342                        // `(map)[1]`, and every grammar that starts a map literal
343                        // requires a `[` right after the keyword.)
344                        || kw == LIST
345                        // `DEALLOCATE [PREPARE] <name>` accepts an optional
346                        // `PREPARE` keyword before the name, so a bare `prepare`
347                        // name is consumed as that keyword on reparse, leaving no
348                        // name (`DEALLOCATE prepare` -> `DEALLOCATE` + the optional
349                        // keyword + a missing name).
350                        || kw == PREPARE
351                        // `CASE` treats a leading `WHEN` as the start of the
352                        // first arm (a searched `CASE` with no operand), so a
353                        // bare `when` identifier used as the `CASE` operand —
354                        // `CASE when.a WHEN ...` — reparses as `CASE WHEN .a ...`
355                        // ("expected an expression, found dot"). Quoting it keeps
356                        // the operand an identifier.
357                        || kw == WHEN
358                        // `COPY [INTO] <table> FROM …` accepts an optional `INTO`
359                        // keyword before the relation name, so a bare `into`
360                        // relation is consumed as that keyword on reparse
361                        // (`COPY into FROM x` -> `COPY INTO <name=from> …`, which
362                        // then fails expecting the FROM/TO direction).
363                        || kw == INTO
364                        // The `IF [NOT] EXISTS` clauses of CREATE/DROP/ALTER sit
365                        // exactly where the object name goes, so a bare `if` name
366                        // is consumed as the start of such a clause on reparse
367                        // (`CREATE CLUSTER if (SIZE …)` -> "expected NOT, found
368                        // left parenthesis").
369                        || kw == IF
370                })
371                .unwrap_or(false)
372    }
373
374    pub fn as_str(&self) -> &str {
375        &self.0
376    }
377
378    pub fn as_keyword(&self) -> Option<Keyword> {
379        self.0.parse().ok()
380    }
381
382    pub fn into_string(self) -> String {
383        self.0
384    }
385}
386
387/// More-or-less a direct translation of the Postgres function for doing the same thing:
388///
389///   <https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/ruleutils.c#L10730-L10812>
390///
391/// Quotation is forced when printing in Stable mode.
392impl AstDisplay for Ident {
393    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
394        if self.can_be_printed_bare() && !f.stable() {
395            f.write_str(&self.0);
396        } else {
397            f.write_str("\"");
398            for ch in self.0.chars() {
399                // Double up on double-quotes.
400                if ch == '"' {
401                    f.write_str("\"");
402                }
403                f.write_str(ch);
404            }
405            f.write_str("\"");
406        }
407    }
408}
409impl_display!(Ident);
410
411#[derive(Clone, Debug, thiserror::Error)]
412pub enum IdentError {
413    #[error("identifier too long (len: {}, max: {}, value: {})", .0.len(), Ident::MAX_LENGTH, .0.quoted())]
414    TooLong(String),
415    #[error(
416        "failed to generate identifier with prefix '{prefix}' and suffix '{suffix}' after {attempts} attempts"
417    )]
418    FailedToGenerate {
419        prefix: String,
420        suffix: String,
421        attempts: usize,
422    },
423
424    #[error("invalid identifier: {}", .0.quoted())]
425    Invalid(String),
426}
427
428/// A name of a table, view, custom type, etc. that lives in a schema, possibly multi-part, i.e. db.schema.obj
429#[derive(
430    Debug,
431    Clone,
432    PartialEq,
433    Eq,
434    Hash,
435    PartialOrd,
436    Ord,
437    Serialize,
438    Deserialize
439)]
440pub struct UnresolvedItemName(pub Vec<Ident>);
441
442pub enum CatalogName {
443    ItemName(Vec<Ident>),
444    FuncName(Vec<Ident>),
445}
446
447impl UnresolvedItemName {
448    /// Creates an `ItemName` with a single [`Ident`], i.e. it appears as
449    /// "unqualified".
450    pub fn unqualified(ident: Ident) -> UnresolvedItemName {
451        UnresolvedItemName(vec![ident])
452    }
453
454    /// Creates an `ItemName` with an [`Ident`] for each element of `n`.
455    ///
456    /// Panics if passed an in ineligible `&[&str]` whose length is 0 or greater
457    /// than 3.
458    pub fn qualified(n: &[Ident]) -> UnresolvedItemName {
459        assert!(n.len() <= 3 && n.len() > 0);
460        UnresolvedItemName(n.iter().cloned().collect::<Vec<_>>())
461    }
462}
463
464impl AstDisplay for UnresolvedItemName {
465    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
466        display::separated(&self.0, ".").fmt(f);
467    }
468}
469impl_display!(UnresolvedItemName);
470
471impl AstDisplay for &UnresolvedItemName {
472    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
473        display::separated(&self.0, ".").fmt(f);
474    }
475}
476
477/// A name of a schema
478#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
479pub struct UnresolvedSchemaName(pub Vec<Ident>);
480
481impl AstDisplay for UnresolvedSchemaName {
482    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
483        display::separated(&self.0, ".").fmt(f);
484    }
485}
486impl_display!(UnresolvedSchemaName);
487
488/// A name of a database
489#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
490pub struct UnresolvedDatabaseName(pub Ident);
491
492impl AstDisplay for UnresolvedDatabaseName {
493    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
494        f.write_node(&self.0);
495    }
496}
497impl_display!(UnresolvedDatabaseName);
498
499// The name of an item not yet created during name resolution, which should be
500// resolveable as an item name later.
501#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
502pub enum DeferredItemName<T: AstInfo> {
503    Named(T::ItemName),
504    Deferred(UnresolvedItemName),
505}
506
507impl<T: AstInfo> AstDisplay for DeferredItemName<T> {
508    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
509        match self {
510            DeferredItemName::Named(o) => f.write_node(o),
511            DeferredItemName::Deferred(o) => f.write_node(o),
512        }
513    }
514}
515impl_display_t!(DeferredItemName);
516
517#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
518pub enum UnresolvedObjectName {
519    Cluster(Ident),
520    ClusterReplica(QualifiedReplica),
521    Database(UnresolvedDatabaseName),
522    Schema(UnresolvedSchemaName),
523    Role(Ident),
524    Item(UnresolvedItemName),
525    NetworkPolicy(Ident),
526}
527
528impl AstDisplay for UnresolvedObjectName {
529    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
530        match self {
531            UnresolvedObjectName::Cluster(n) => f.write_node(n),
532            UnresolvedObjectName::ClusterReplica(n) => f.write_node(n),
533            UnresolvedObjectName::Database(n) => f.write_node(n),
534            UnresolvedObjectName::Schema(n) => f.write_node(n),
535            UnresolvedObjectName::Role(n) => f.write_node(n),
536            UnresolvedObjectName::Item(n) => f.write_node(n),
537            UnresolvedObjectName::NetworkPolicy(n) => f.write_node(n),
538        }
539    }
540}
541impl_display!(UnresolvedObjectName);