Skip to main content

mz_sql/
names.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Structured name types for SQL objects.
11
12use std::borrow::Cow;
13use std::collections::{BTreeMap, BTreeSet};
14use std::fmt;
15use std::str::FromStr;
16use std::sync::LazyLock;
17
18use anyhow::anyhow;
19use mz_controller_types::{ClusterId, ReplicaId};
20use mz_expr::LocalId;
21use mz_ore::str::StrExt;
22use mz_repr::network_policy_id::NetworkPolicyId;
23use mz_repr::role_id::RoleId;
24use mz_repr::{CatalogItemId, GlobalId, RelationVersion};
25use mz_repr::{ColumnName, RelationVersionSelector};
26use mz_sql_parser::ast::visit_mut::VisitMutNode;
27use mz_sql_parser::ast::{Expr, RawNetworkPolicyName, Version};
28use mz_sql_parser::ident;
29use proptest_derive::Arbitrary;
30use serde::{Deserialize, Serialize};
31use uncased::UncasedStr;
32
33use crate::ast::display::{AstDisplay, AstFormatter};
34use crate::ast::fold::{Fold, FoldNode};
35use crate::ast::visit::{Visit, VisitNode};
36use crate::ast::visit_mut::VisitMut;
37use crate::ast::{
38    self, AstInfo, Cte, CteBlock, CteMutRec, DocOnIdentifier, GrantTargetSpecification,
39    GrantTargetSpecificationInner, Ident, MutRecBlock, ObjectType, Query, Raw, RawClusterName,
40    RawDataType, RawItemName, Statement, UnresolvedItemName, UnresolvedObjectName,
41};
42use crate::catalog::{
43    CatalogError, CatalogItem, CatalogItemType, CatalogTypeDetails, SessionCatalog,
44};
45use crate::normalize;
46use crate::plan::PlanError;
47
48/// A fully-qualified human readable name of an item in the catalog.
49///
50/// Catalog names compare case sensitively. Use
51/// [`normalize::unresolved_item_name`] to
52/// perform proper case folding if converting an [`UnresolvedItemName`] to a
53/// `FullItemName`.
54///
55/// [`normalize::unresolved_item_name`]: crate::normalize::unresolved_item_name
56#[derive(
57    Debug,
58    Clone,
59    Eq,
60    PartialEq,
61    Hash,
62    Ord,
63    PartialOrd,
64    Serialize,
65    Deserialize
66)]
67pub struct FullItemName {
68    /// The database name.
69    pub database: RawDatabaseSpecifier,
70    /// The schema name.
71    pub schema: String,
72    /// The item name.
73    pub item: String,
74}
75
76impl FullItemName {
77    /// Converts the name into a string vector of its constituent parts:
78    /// database (if present), schema, and item.
79    pub fn into_parts(self) -> Vec<String> {
80        let mut parts = vec![];
81        if let RawDatabaseSpecifier::Name(name) = self.database {
82            parts.push(name);
83        }
84        parts.push(self.schema);
85        parts.push(self.item);
86        parts
87    }
88}
89
90impl fmt::Display for FullItemName {
91    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92        if let RawDatabaseSpecifier::Name(database) = &self.database {
93            write!(f, "{}.", database)?;
94        }
95        write!(f, "{}.{}", self.schema, self.item)
96    }
97}
98
99impl From<FullItemName> for UnresolvedItemName {
100    fn from(full_name: FullItemName) -> UnresolvedItemName {
101        // TODO(parkmycar): Change UnresolvedItemName to use `Ident` internally.
102        let mut name_parts = Vec::new();
103        if let RawDatabaseSpecifier::Name(database) = full_name.database {
104            name_parts.push(Ident::new_unchecked(database));
105        }
106        name_parts.push(Ident::new_unchecked(full_name.schema));
107        name_parts.push(Ident::new_unchecked(full_name.item));
108        UnresolvedItemName(name_parts)
109    }
110}
111
112/// A fully-qualified non-human readable name of an item in the catalog using IDs for the database
113/// and schema.
114#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
115pub struct QualifiedItemName {
116    pub qualifiers: ItemQualifiers,
117    pub item: String,
118}
119
120// Do not implement [`Display`] for [`QualifiedItemName`]. [`FullItemName`] should always be
121// displayed instead.
122static_assertions::assert_not_impl_any!(QualifiedItemName: fmt::Display);
123
124/// An optionally-qualified human-readable name of an item in the catalog.
125///
126/// This is like a [`FullItemName`], but either the database or schema name may be
127/// omitted.
128#[derive(
129    Clone,
130    Debug,
131    Serialize,
132    Deserialize,
133    PartialEq,
134    Eq,
135    PartialOrd,
136    Ord,
137    Hash
138)]
139pub struct PartialItemName {
140    pub database: Option<String>,
141    pub schema: Option<String>,
142    pub item: String,
143}
144
145impl PartialItemName {
146    // Whether either self or other might be a (possibly differently qualified)
147    // version of the other.
148    pub fn matches(&self, other: &Self) -> bool {
149        match (&self.database, &other.database) {
150            (Some(d1), Some(d2)) if d1 != d2 => return false,
151            _ => (),
152        }
153        match (&self.schema, &other.schema) {
154            (Some(s1), Some(s2)) if s1 != s2 => return false,
155            _ => (),
156        }
157        self.item == other.item
158    }
159}
160
161impl fmt::Display for PartialItemName {
162    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163        if let Some(database) = &self.database {
164            write!(f, "{}.", database)?;
165        }
166        if let Some(schema) = &self.schema {
167            write!(f, "{}.", schema)?;
168        }
169        write!(f, "{}", self.item)
170    }
171}
172
173impl From<FullItemName> for PartialItemName {
174    fn from(n: FullItemName) -> PartialItemName {
175        let database = match n.database {
176            RawDatabaseSpecifier::Ambient => None,
177            RawDatabaseSpecifier::Name(name) => Some(name),
178        };
179        PartialItemName {
180            database,
181            schema: Some(n.schema),
182            item: n.item,
183        }
184    }
185}
186
187impl From<String> for PartialItemName {
188    fn from(item: String) -> Self {
189        PartialItemName {
190            database: None,
191            schema: None,
192            item,
193        }
194    }
195}
196
197impl From<PartialItemName> for UnresolvedItemName {
198    fn from(partial_name: PartialItemName) -> UnresolvedItemName {
199        // TODO(parkmycar): Change UnresolvedItemName to use `Ident` internally.
200        let mut name_parts = Vec::new();
201        if let Some(database) = partial_name.database {
202            name_parts.push(Ident::new_unchecked(database));
203        }
204        if let Some(schema) = partial_name.schema {
205            name_parts.push(Ident::new_unchecked(schema));
206        }
207        name_parts.push(Ident::new_unchecked(partial_name.item));
208        UnresolvedItemName(name_parts)
209    }
210}
211
212/// A fully-qualified human readable name of a schema in the catalog.
213#[derive(
214    Debug,
215    Clone,
216    Eq,
217    PartialEq,
218    Hash,
219    PartialOrd,
220    Ord,
221    Serialize,
222    Deserialize
223)]
224pub struct FullSchemaName {
225    /// The database name
226    pub database: RawDatabaseSpecifier,
227    /// The schema name
228    pub schema: String,
229}
230
231impl fmt::Display for FullSchemaName {
232    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233        if let RawDatabaseSpecifier::Name(database) = &self.database {
234            write!(f, "{}.", database)?;
235        }
236        write!(f, "{}", self.schema)
237    }
238}
239
240/// The fully-qualified non-human readable name of a schema in the catalog.
241#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
242pub struct QualifiedSchemaName {
243    pub database: ResolvedDatabaseSpecifier,
244    pub schema: String,
245}
246
247impl fmt::Display for QualifiedSchemaName {
248    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249        match &self.database {
250            ResolvedDatabaseSpecifier::Ambient => f.write_str(&self.schema),
251            ResolvedDatabaseSpecifier::Id(id) => write!(f, "{}.{}", id, self.schema),
252        }
253    }
254}
255
256/// An optionally-qualified name of an schema in the catalog.
257///
258/// This is like a [`FullSchemaName`], but either the database or schema name may be
259/// omitted.
260#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
261pub struct PartialSchemaName {
262    pub database: Option<String>,
263    pub schema: String,
264}
265
266impl fmt::Display for PartialSchemaName {
267    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268        if let Some(database) = &self.database {
269            write!(f, "{}.", database)?;
270        }
271        write!(f, "{}", self.schema)
272    }
273}
274
275/// A human readable name of a database.
276#[derive(
277    Debug,
278    Clone,
279    Eq,
280    PartialEq,
281    Ord,
282    PartialOrd,
283    Hash,
284    Serialize,
285    Deserialize
286)]
287pub enum RawDatabaseSpecifier {
288    /// The "ambient" database, which is always present and is not named
289    /// explicitly, but by omission.
290    Ambient,
291    /// A normal database with a name.
292    Name(String),
293}
294
295impl fmt::Display for RawDatabaseSpecifier {
296    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297        match self {
298            Self::Ambient => f.write_str("<none>"),
299            Self::Name(name) => f.write_str(name),
300        }
301    }
302}
303
304impl From<Option<String>> for RawDatabaseSpecifier {
305    fn from(s: Option<String>) -> RawDatabaseSpecifier {
306        match s {
307            None => Self::Ambient,
308            Some(name) => Self::Name(name),
309        }
310    }
311}
312
313/// An id of a database.
314#[derive(
315    Debug,
316    Clone,
317    Copy,
318    Eq,
319    PartialEq,
320    Hash,
321    PartialOrd,
322    Ord,
323    Serialize,
324    Deserialize,
325    Arbitrary
326)]
327pub enum ResolvedDatabaseSpecifier {
328    /// The "ambient" database, which is always present and is not named
329    /// explicitly, but by omission.
330    Ambient,
331    /// A normal database with a name.
332    Id(DatabaseId),
333}
334
335impl ResolvedDatabaseSpecifier {
336    pub fn id(&self) -> Option<DatabaseId> {
337        match self {
338            ResolvedDatabaseSpecifier::Ambient => None,
339            ResolvedDatabaseSpecifier::Id(id) => Some(*id),
340        }
341    }
342}
343
344impl fmt::Display for ResolvedDatabaseSpecifier {
345    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346        match self {
347            Self::Ambient => f.write_str("<none>"),
348            Self::Id(id) => write!(f, "{}", id),
349        }
350    }
351}
352
353impl AstDisplay for ResolvedDatabaseSpecifier {
354    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
355        f.write_str(format!("{}", self));
356    }
357}
358
359impl From<DatabaseId> for ResolvedDatabaseSpecifier {
360    fn from(id: DatabaseId) -> Self {
361        Self::Id(id)
362    }
363}
364
365impl From<Option<DatabaseId>> for ResolvedDatabaseSpecifier {
366    fn from(id: Option<DatabaseId>) -> Self {
367        match id {
368            Some(id) => Self::Id(id),
369            None => Self::Ambient,
370        }
371    }
372}
373
374/*
375 * TODO(jkosh44) It's possible that in order to fix
376 * https://github.com/MaterializeInc/database-issues/issues/2689 we will need to assign temporary
377 * schemas unique Ids. If/when that happens we can remove this enum and refer to all schemas by
378 * their Id.
379 */
380/// An id of a schema.
381#[derive(
382    Debug,
383    Clone,
384    Copy,
385    Eq,
386    PartialEq,
387    Hash,
388    PartialOrd,
389    Ord,
390    Serialize,
391    Deserialize
392)]
393pub enum SchemaSpecifier {
394    /// A temporary schema
395    Temporary,
396    /// A normal database with a name.
397    Id(SchemaId),
398}
399
400impl SchemaSpecifier {
401    const TEMPORARY_SCHEMA_ID: u64 = 0;
402
403    pub fn is_system(&self) -> bool {
404        match self {
405            SchemaSpecifier::Temporary => false,
406            SchemaSpecifier::Id(id) => id.is_system(),
407        }
408    }
409
410    pub fn is_user(&self) -> bool {
411        match self {
412            SchemaSpecifier::Temporary => true,
413            SchemaSpecifier::Id(id) => id.is_user(),
414        }
415    }
416
417    pub fn is_temporary(&self) -> bool {
418        matches!(self, SchemaSpecifier::Temporary)
419    }
420}
421
422impl fmt::Display for SchemaSpecifier {
423    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424        match self {
425            Self::Temporary => f.write_str(format!("{}", Self::TEMPORARY_SCHEMA_ID).as_str()),
426            Self::Id(id) => write!(f, "{}", id),
427        }
428    }
429}
430
431impl AstDisplay for SchemaSpecifier {
432    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
433        f.write_str(format!("{}", self));
434    }
435}
436
437impl From<SchemaId> for SchemaSpecifier {
438    fn from(id: SchemaId) -> SchemaSpecifier {
439        match id {
440            SchemaId::User(id) if id == SchemaSpecifier::TEMPORARY_SCHEMA_ID => {
441                SchemaSpecifier::Temporary
442            }
443            schema_id => SchemaSpecifier::Id(schema_id),
444        }
445    }
446}
447
448impl From<&SchemaSpecifier> for SchemaId {
449    fn from(schema_spec: &SchemaSpecifier) -> Self {
450        match schema_spec {
451            SchemaSpecifier::Temporary => SchemaId::User(SchemaSpecifier::TEMPORARY_SCHEMA_ID),
452            SchemaSpecifier::Id(id) => id.clone(),
453        }
454    }
455}
456
457impl From<SchemaSpecifier> for SchemaId {
458    fn from(schema_spec: SchemaSpecifier) -> Self {
459        match schema_spec {
460            SchemaSpecifier::Temporary => SchemaId::User(SchemaSpecifier::TEMPORARY_SCHEMA_ID),
461            SchemaSpecifier::Id(id) => id,
462        }
463    }
464}
465
466// Aug is the type variable assigned to an AST that has already been
467// name-resolved. An AST in this state has global IDs populated next to table
468// names, and local IDs assigned to CTE definitions and references.
469#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone, Default)]
470pub struct Aug;
471
472#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)]
473pub struct ItemQualifiers {
474    pub database_spec: ResolvedDatabaseSpecifier,
475    pub schema_spec: SchemaSpecifier,
476}
477
478#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
479pub enum ResolvedItemName {
480    Item {
481        id: CatalogItemId,
482        qualifiers: ItemQualifiers,
483        full_name: FullItemName,
484        // Whether this object, when printed out, should use [id AS name] syntax. We
485        // want this for things like tables and sources, but not for things like
486        // types.
487        print_id: bool,
488        version: RelationVersionSelector,
489    },
490    Cte {
491        id: LocalId,
492        name: String,
493    },
494    Error,
495}
496
497impl ResolvedItemName {
498    pub fn full_name_str(&self) -> String {
499        match self {
500            ResolvedItemName::Item { full_name, .. } => full_name.to_string(),
501            ResolvedItemName::Cte { name, .. } => name.clone(),
502            ResolvedItemName::Error => "error in name resolution".to_string(),
503        }
504    }
505
506    pub fn full_item_name(&self) -> &FullItemName {
507        match self {
508            ResolvedItemName::Item { full_name, .. } => full_name,
509            _ => panic!("cannot call object_full_name on non-object"),
510        }
511    }
512
513    pub fn item_id(&self) -> &CatalogItemId {
514        match self {
515            ResolvedItemName::Item { id, .. } => id,
516            _ => panic!("cannot call item_id on non-object"),
517        }
518    }
519
520    pub fn version(&self) -> &RelationVersionSelector {
521        match self {
522            ResolvedItemName::Item { version, .. } => version,
523            _ => panic!("cannot call version on non-object"),
524        }
525    }
526}
527
528impl AstDisplay for ResolvedItemName {
529    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
530        match self {
531            ResolvedItemName::Item {
532                id,
533                qualifiers: _,
534                full_name,
535                print_id,
536                version,
537            } => {
538                if *print_id {
539                    f.write_str(format!("[{} AS ", id));
540                }
541                if let RawDatabaseSpecifier::Name(database) = &full_name.database {
542                    f.write_node(&Ident::new_unchecked(database));
543                    f.write_str(".");
544                }
545                f.write_node(&Ident::new_unchecked(&full_name.schema));
546                f.write_str(".");
547                f.write_node(&Ident::new_unchecked(&full_name.item));
548
549                if *print_id {
550                    if let RelationVersionSelector::Specific(version) = version {
551                        let version: Version = (*version).into();
552                        f.write_str(" VERSION ");
553                        f.write_node(&version);
554                    }
555                }
556
557                if *print_id {
558                    f.write_str("]");
559                }
560            }
561            ResolvedItemName::Cte { name, .. } => f.write_node(&Ident::new_unchecked(name)),
562            ResolvedItemName::Error => {}
563        }
564    }
565}
566
567impl std::fmt::Display for ResolvedItemName {
568    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
569        f.write_str(self.to_ast_string_simple().as_str())
570    }
571}
572
573#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
574pub enum ResolvedColumnReference {
575    Column { name: ColumnName, index: usize },
576    Error,
577}
578
579impl AstDisplay for ResolvedColumnReference {
580    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
581        match self {
582            ResolvedColumnReference::Column { name, .. } => {
583                f.write_node(&Ident::new_unchecked(name.as_str()));
584            }
585            ResolvedColumnReference::Error => {}
586        }
587    }
588}
589
590impl std::fmt::Display for ResolvedColumnReference {
591    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
592        f.write_str(self.to_ast_string_simple().as_str())
593    }
594}
595
596#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
597pub enum ResolvedSchemaName {
598    Schema {
599        database_spec: ResolvedDatabaseSpecifier,
600        schema_spec: SchemaSpecifier,
601        full_name: FullSchemaName,
602    },
603    Error,
604}
605
606impl ResolvedSchemaName {
607    /// Panics if this is `Self::Error`.
608    pub fn database_spec(&self) -> &ResolvedDatabaseSpecifier {
609        match self {
610            ResolvedSchemaName::Schema { database_spec, .. } => database_spec,
611            ResolvedSchemaName::Error => {
612                unreachable!("should have been handled by name resolution")
613            }
614        }
615    }
616
617    /// Panics if this is `Self::Error`.
618    pub fn schema_spec(&self) -> &SchemaSpecifier {
619        match self {
620            ResolvedSchemaName::Schema { schema_spec, .. } => schema_spec,
621            ResolvedSchemaName::Error => {
622                unreachable!("should have been handled by name resolution")
623            }
624        }
625    }
626}
627
628impl AstDisplay for ResolvedSchemaName {
629    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
630        match self {
631            ResolvedSchemaName::Schema { full_name, .. } => {
632                if let RawDatabaseSpecifier::Name(database) = &full_name.database {
633                    f.write_node(&Ident::new_unchecked(database));
634                    f.write_str(".");
635                }
636                f.write_node(&Ident::new_unchecked(&full_name.schema));
637            }
638            ResolvedSchemaName::Error => {}
639        }
640    }
641}
642
643#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
644pub enum ResolvedDatabaseName {
645    Database { id: DatabaseId, name: String },
646    Error,
647}
648
649impl ResolvedDatabaseName {
650    /// Panics if this is `Self::Error`.
651    pub fn database_id(&self) -> &DatabaseId {
652        match self {
653            ResolvedDatabaseName::Database { id, .. } => id,
654            ResolvedDatabaseName::Error => {
655                unreachable!("should have been handled by name resolution")
656            }
657        }
658    }
659}
660
661impl AstDisplay for ResolvedDatabaseName {
662    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
663        match self {
664            ResolvedDatabaseName::Database { name, .. } => {
665                f.write_node(&Ident::new_unchecked(name))
666            }
667            ResolvedDatabaseName::Error => {}
668        }
669    }
670}
671
672#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
673pub struct ResolvedClusterName {
674    pub id: ClusterId,
675    /// If set, a name to print in the `AstDisplay` implementation instead of
676    /// `None`. This is only meant to be used by the `NameSimplifier`.
677    ///
678    /// NOTE(benesch): it would be much clearer if the `NameSimplifier` folded
679    /// the AST into a different metadata type, to avoid polluting the resolved
680    /// AST with this field.
681    pub print_name: Option<String>,
682}
683
684impl AstDisplay for ResolvedClusterName {
685    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
686        if let Some(print_name) = &self.print_name {
687            f.write_node(&Ident::new_unchecked(print_name))
688        } else {
689            f.write_str(format!("[{}]", self.id))
690        }
691    }
692}
693
694#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
695pub struct ResolvedClusterReplicaName {
696    pub cluster_id: ClusterId,
697    pub replica_id: ReplicaId,
698}
699
700impl AstDisplay for ResolvedClusterReplicaName {
701    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
702        f.write_str(format!("[{}.{}]", self.cluster_id, self.replica_id))
703    }
704}
705
706#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
707pub enum ResolvedDataType {
708    AnonymousList(Box<ResolvedDataType>),
709    AnonymousMap {
710        key_type: Box<ResolvedDataType>,
711        value_type: Box<ResolvedDataType>,
712    },
713    Named {
714        id: CatalogItemId,
715        qualifiers: ItemQualifiers,
716        full_name: FullItemName,
717        modifiers: Vec<i64>,
718        print_id: bool,
719    },
720    Error,
721}
722
723impl AstDisplay for ResolvedDataType {
724    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
725        match self {
726            ResolvedDataType::AnonymousList(element_type) => {
727                element_type.fmt(f);
728                f.write_str(" list");
729            }
730            ResolvedDataType::AnonymousMap {
731                key_type,
732                value_type,
733            } => {
734                f.write_str("map[");
735                key_type.fmt(f);
736                f.write_str("=>");
737                value_type.fmt(f);
738                f.write_str("]");
739            }
740            ResolvedDataType::Named {
741                id,
742                full_name,
743                modifiers,
744                print_id,
745                ..
746            } => {
747                if *print_id {
748                    f.write_str(format!("[{} AS ", id));
749                }
750                if let RawDatabaseSpecifier::Name(database) = &full_name.database {
751                    f.write_node(&Ident::new_unchecked(database));
752                    f.write_str(".");
753                }
754
755                f.write_node(&Ident::new_unchecked(&full_name.schema));
756                f.write_str(".");
757
758                f.write_node(&Ident::new_unchecked(&full_name.item));
759                if *print_id {
760                    f.write_str("]");
761                }
762                if modifiers.len() > 0 {
763                    f.write_str("(");
764                    f.write_node(&ast::display::comma_separated(modifiers));
765                    f.write_str(")");
766                }
767            }
768            ResolvedDataType::Error => {}
769        }
770    }
771}
772
773impl ResolvedDataType {
774    /// Return the name of `self`'s item without qualification or IDs.
775    ///
776    /// This is used to generate default column names for cast operations.
777    pub fn unqualified_item_name(&self) -> String {
778        let mut res = String::new();
779        match self {
780            ResolvedDataType::AnonymousList(element_type) => {
781                res += &element_type.unqualified_item_name();
782                res += " list";
783            }
784            ResolvedDataType::AnonymousMap {
785                key_type,
786                value_type,
787            } => {
788                res += "map[";
789                res += &key_type.unqualified_item_name();
790                res += "=>";
791                res += &value_type.unqualified_item_name();
792                res += "]";
793            }
794            ResolvedDataType::Named { full_name, .. } => {
795                res += &full_name.item;
796            }
797            ResolvedDataType::Error => {}
798        }
799        res
800    }
801
802    /// Return the name of `self`'s without IDs or modifiers.
803    ///
804    /// This is used for error messages.
805    pub fn human_readable_name(&self) -> String {
806        let mut res = String::new();
807        match self {
808            ResolvedDataType::AnonymousList(element_type) => {
809                res += &element_type.human_readable_name();
810                res += " list";
811            }
812            ResolvedDataType::AnonymousMap {
813                key_type,
814                value_type,
815            } => {
816                res += "map[";
817                res += &key_type.human_readable_name();
818                res += "=>";
819                res += &value_type.human_readable_name();
820                res += "]";
821            }
822            ResolvedDataType::Named { full_name, .. } => {
823                if let RawDatabaseSpecifier::Name(database) = &full_name.database {
824                    res += database;
825                    res += ".";
826                }
827                res += &full_name.schema;
828                res += ".";
829                res += &full_name.item;
830            }
831            ResolvedDataType::Error => {}
832        }
833        res
834    }
835}
836
837impl fmt::Display for ResolvedDataType {
838    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
839        f.write_str(self.to_ast_string_simple().as_str())
840    }
841}
842
843#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
844pub struct ResolvedRoleName {
845    pub id: RoleId,
846    pub name: String,
847}
848
849impl AstDisplay for ResolvedRoleName {
850    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
851        f.write_str(format!("[{} AS {}]", self.id, self.name));
852    }
853}
854
855#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
856pub struct ResolvedNetworkPolicyName {
857    pub id: NetworkPolicyId,
858    pub name: String,
859}
860
861impl AstDisplay for ResolvedNetworkPolicyName {
862    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
863        f.write_str(format!("[{} AS {}]", self.id, self.name));
864    }
865}
866
867#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
868pub enum ResolvedObjectName {
869    Cluster(ResolvedClusterName),
870    ClusterReplica(ResolvedClusterReplicaName),
871    Database(ResolvedDatabaseName),
872    Schema(ResolvedSchemaName),
873    Role(ResolvedRoleName),
874    NetworkPolicy(ResolvedNetworkPolicyName),
875    Item(ResolvedItemName),
876}
877
878impl AstDisplay for ResolvedObjectName {
879    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
880        match self {
881            ResolvedObjectName::Cluster(n) => f.write_node(n),
882            ResolvedObjectName::ClusterReplica(n) => f.write_node(n),
883            ResolvedObjectName::Database(n) => f.write_node(n),
884            ResolvedObjectName::Schema(n) => f.write_node(n),
885            ResolvedObjectName::Role(n) => f.write_node(n),
886            ResolvedObjectName::Item(n) => f.write_node(n),
887            ResolvedObjectName::NetworkPolicy(n) => f.write_node(n),
888        }
889    }
890}
891
892impl AstInfo for Aug {
893    type NestedStatement = Statement<Raw>;
894    type ItemName = ResolvedItemName;
895    type ColumnReference = ResolvedColumnReference;
896    type SchemaName = ResolvedSchemaName;
897    type DatabaseName = ResolvedDatabaseName;
898    type ClusterName = ResolvedClusterName;
899    type DataType = ResolvedDataType;
900    type CteId = LocalId;
901    type RoleName = ResolvedRoleName;
902    type ObjectName = ResolvedObjectName;
903    type NetworkPolicyName = ResolvedNetworkPolicyName;
904}
905
906/// The identifier for a schema.
907#[derive(
908    Clone,
909    Copy,
910    Debug,
911    Eq,
912    PartialEq,
913    Ord,
914    PartialOrd,
915    Hash,
916    Serialize,
917    Deserialize,
918    Arbitrary
919)]
920pub enum SchemaId {
921    User(u64),
922    System(u64),
923}
924
925impl SchemaId {
926    pub fn is_user(&self) -> bool {
927        matches!(self, SchemaId::User(_))
928    }
929
930    pub fn is_system(&self) -> bool {
931        matches!(self, SchemaId::System(_))
932    }
933}
934
935impl fmt::Display for SchemaId {
936    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
937        match self {
938            SchemaId::System(id) => write!(f, "s{}", id),
939            SchemaId::User(id) => write!(f, "u{}", id),
940        }
941    }
942}
943
944impl FromStr for SchemaId {
945    type Err = PlanError;
946
947    fn from_str(s: &str) -> Result<Self, Self::Err> {
948        let err = || PlanError::Unstructured(format!("couldn't parse SchemaId {}", s));
949        // Validate the (single-byte, ASCII) tag before slicing so that a
950        // multi-byte leading character doesn't slice inside a UTF-8 boundary.
951        let variant = match s.chars().next() {
952            Some('s') => SchemaId::System,
953            Some('u') => SchemaId::User,
954            _ => return Err(err()),
955        };
956        let val: u64 = s[1..].parse().map_err(|_| err())?;
957        Ok(variant(val))
958    }
959}
960
961/// The identifier for a database.
962#[derive(
963    Clone,
964    Copy,
965    Debug,
966    Eq,
967    PartialEq,
968    Ord,
969    PartialOrd,
970    Hash,
971    Serialize,
972    Deserialize,
973    Arbitrary
974)]
975pub enum DatabaseId {
976    User(u64),
977    System(u64),
978}
979
980impl DatabaseId {
981    pub fn is_user(&self) -> bool {
982        matches!(self, DatabaseId::User(_))
983    }
984
985    pub fn is_system(&self) -> bool {
986        matches!(self, DatabaseId::System(_))
987    }
988}
989
990impl fmt::Display for DatabaseId {
991    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
992        match self {
993            DatabaseId::System(id) => write!(f, "s{}", id),
994            DatabaseId::User(id) => write!(f, "u{}", id),
995        }
996    }
997}
998
999impl FromStr for DatabaseId {
1000    type Err = PlanError;
1001
1002    fn from_str(s: &str) -> Result<Self, Self::Err> {
1003        let err = || PlanError::Unstructured(format!("couldn't parse DatabaseId {}", s));
1004        // Validate the (single-byte, ASCII) tag before slicing so that a
1005        // multi-byte leading character doesn't slice inside a UTF-8 boundary.
1006        let variant = match s.chars().next() {
1007            Some('s') => DatabaseId::System,
1008            Some('u') => DatabaseId::User,
1009            _ => return Err(err()),
1010        };
1011        let val: u64 = s[1..].parse().map_err(|_| err())?;
1012        Ok(variant(val))
1013    }
1014}
1015
1016pub static PUBLIC_ROLE_NAME: LazyLock<&UncasedStr> = LazyLock::new(|| UncasedStr::new("PUBLIC"));
1017
1018#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1019pub enum ObjectId {
1020    Cluster(ClusterId),
1021    ClusterReplica((ClusterId, ReplicaId)),
1022    Database(DatabaseId),
1023    Schema((ResolvedDatabaseSpecifier, SchemaSpecifier)),
1024    Role(RoleId),
1025    Item(CatalogItemId),
1026    NetworkPolicy(NetworkPolicyId),
1027}
1028
1029impl ObjectId {
1030    pub fn unwrap_cluster_id(self) -> ClusterId {
1031        match self {
1032            ObjectId::Cluster(id) => id,
1033            _ => panic!("ObjectId::unwrap_cluster_id called on {self:?}"),
1034        }
1035    }
1036    pub fn unwrap_cluster_replica_id(self) -> (ClusterId, ReplicaId) {
1037        match self {
1038            ObjectId::ClusterReplica(id) => id,
1039            _ => panic!("ObjectId::unwrap_cluster_replica_id called on {self:?}"),
1040        }
1041    }
1042    pub fn unwrap_database_id(self) -> DatabaseId {
1043        match self {
1044            ObjectId::Database(id) => id,
1045            _ => panic!("ObjectId::unwrap_database_id called on {self:?}"),
1046        }
1047    }
1048    pub fn unwrap_schema_id(self) -> (ResolvedDatabaseSpecifier, SchemaSpecifier) {
1049        match self {
1050            ObjectId::Schema(id) => id,
1051            _ => panic!("ObjectId::unwrap_schema_id called on {self:?}"),
1052        }
1053    }
1054    pub fn unwrap_role_id(self) -> RoleId {
1055        match self {
1056            ObjectId::Role(id) => id,
1057            _ => panic!("ObjectId::unwrap_role_id called on {self:?}"),
1058        }
1059    }
1060    pub fn unwrap_item_id(self) -> CatalogItemId {
1061        match self {
1062            ObjectId::Item(id) => id,
1063            _ => panic!("ObjectId::unwrap_item_id called on {self:?}"),
1064        }
1065    }
1066
1067    pub fn is_system(&self) -> bool {
1068        match self {
1069            ObjectId::Cluster(cluster_id) => cluster_id.is_system(),
1070            ObjectId::ClusterReplica((_cluster_id, replica_id)) => replica_id.is_system(),
1071            ObjectId::Database(database_id) => database_id.is_system(),
1072            ObjectId::Schema((_database_id, schema_id)) => schema_id.is_system(),
1073            ObjectId::Role(role_id) => role_id.is_system(),
1074            ObjectId::Item(global_id) => global_id.is_system(),
1075            ObjectId::NetworkPolicy(network_policy_id) => network_policy_id.is_system(),
1076        }
1077    }
1078
1079    pub fn is_user(&self) -> bool {
1080        match self {
1081            ObjectId::Cluster(cluster_id) => cluster_id.is_user(),
1082            ObjectId::ClusterReplica((_cluster_id, replica_id)) => replica_id.is_user(),
1083            ObjectId::Database(database_id) => database_id.is_user(),
1084            ObjectId::Schema((_database_id, schema_id)) => schema_id.is_user(),
1085            ObjectId::Role(role_id) => role_id.is_user(),
1086            ObjectId::Item(global_id) => global_id.is_user(),
1087            ObjectId::NetworkPolicy(network_policy_id) => network_policy_id.is_user(),
1088        }
1089    }
1090}
1091
1092impl fmt::Display for ObjectId {
1093    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1094        match self {
1095            ObjectId::Cluster(cluster_id) => write!(f, "C{cluster_id}"),
1096            ObjectId::ClusterReplica((cluster_id, replica_id)) => {
1097                write!(f, "CR{cluster_id}.{replica_id}")
1098            }
1099            ObjectId::Database(database_id) => write!(f, "D{database_id}"),
1100            ObjectId::Schema((database_spec, schema_spec)) => {
1101                let database_id = match database_spec {
1102                    ResolvedDatabaseSpecifier::Ambient => "".to_string(),
1103                    ResolvedDatabaseSpecifier::Id(database_id) => format!("{database_id}."),
1104                };
1105                write!(f, "S{database_id}{schema_spec}")
1106            }
1107            ObjectId::Role(role_id) => write!(f, "R{role_id}"),
1108            ObjectId::Item(item_id) => write!(f, "I{item_id}"),
1109            ObjectId::NetworkPolicy(network_policy_id) => write!(f, "NP{network_policy_id}"),
1110        }
1111    }
1112}
1113
1114impl TryFrom<ResolvedObjectName> for ObjectId {
1115    type Error = anyhow::Error;
1116
1117    fn try_from(name: ResolvedObjectName) -> Result<ObjectId, Self::Error> {
1118        match name {
1119            ResolvedObjectName::Cluster(name) => Ok(ObjectId::Cluster(name.id)),
1120            ResolvedObjectName::ClusterReplica(name) => {
1121                Ok(ObjectId::ClusterReplica((name.cluster_id, name.replica_id)))
1122            }
1123            ResolvedObjectName::Database(name) => Ok(ObjectId::Database(*name.database_id())),
1124            ResolvedObjectName::Schema(name) => match name {
1125                ResolvedSchemaName::Schema {
1126                    database_spec,
1127                    schema_spec,
1128                    ..
1129                } => Ok(ObjectId::Schema((database_spec, schema_spec))),
1130                ResolvedSchemaName::Error => Err(anyhow!("error in name resolution")),
1131            },
1132            ResolvedObjectName::Role(name) => Ok(ObjectId::Role(name.id)),
1133            ResolvedObjectName::Item(name) => match name {
1134                ResolvedItemName::Item { id, .. } => Ok(ObjectId::Item(id)),
1135                ResolvedItemName::Cte { .. } => Err(anyhow!("CTE does not correspond to object")),
1136                ResolvedItemName::Error => Err(anyhow!("error in name resolution")),
1137            },
1138            ResolvedObjectName::NetworkPolicy(name) => Ok(ObjectId::NetworkPolicy(name.id)),
1139        }
1140    }
1141}
1142
1143impl From<ClusterId> for ObjectId {
1144    fn from(id: ClusterId) -> Self {
1145        ObjectId::Cluster(id)
1146    }
1147}
1148
1149impl From<&ClusterId> for ObjectId {
1150    fn from(id: &ClusterId) -> Self {
1151        ObjectId::Cluster(*id)
1152    }
1153}
1154
1155impl From<(ClusterId, ReplicaId)> for ObjectId {
1156    fn from(id: (ClusterId, ReplicaId)) -> Self {
1157        ObjectId::ClusterReplica(id)
1158    }
1159}
1160
1161impl From<&(ClusterId, ReplicaId)> for ObjectId {
1162    fn from(id: &(ClusterId, ReplicaId)) -> Self {
1163        ObjectId::ClusterReplica(*id)
1164    }
1165}
1166
1167impl From<DatabaseId> for ObjectId {
1168    fn from(id: DatabaseId) -> Self {
1169        ObjectId::Database(id)
1170    }
1171}
1172
1173impl From<&DatabaseId> for ObjectId {
1174    fn from(id: &DatabaseId) -> Self {
1175        ObjectId::Database(*id)
1176    }
1177}
1178
1179impl From<ItemQualifiers> for ObjectId {
1180    fn from(qualifiers: ItemQualifiers) -> Self {
1181        ObjectId::Schema((qualifiers.database_spec, qualifiers.schema_spec))
1182    }
1183}
1184
1185impl From<&ItemQualifiers> for ObjectId {
1186    fn from(qualifiers: &ItemQualifiers) -> Self {
1187        ObjectId::Schema((qualifiers.database_spec, qualifiers.schema_spec))
1188    }
1189}
1190
1191impl From<(ResolvedDatabaseSpecifier, SchemaSpecifier)> for ObjectId {
1192    fn from(id: (ResolvedDatabaseSpecifier, SchemaSpecifier)) -> Self {
1193        ObjectId::Schema(id)
1194    }
1195}
1196
1197impl From<&(ResolvedDatabaseSpecifier, SchemaSpecifier)> for ObjectId {
1198    fn from(id: &(ResolvedDatabaseSpecifier, SchemaSpecifier)) -> Self {
1199        ObjectId::Schema(*id)
1200    }
1201}
1202
1203impl From<RoleId> for ObjectId {
1204    fn from(id: RoleId) -> Self {
1205        ObjectId::Role(id)
1206    }
1207}
1208
1209impl From<&RoleId> for ObjectId {
1210    fn from(id: &RoleId) -> Self {
1211        ObjectId::Role(*id)
1212    }
1213}
1214
1215impl From<CatalogItemId> for ObjectId {
1216    fn from(id: CatalogItemId) -> Self {
1217        ObjectId::Item(id)
1218    }
1219}
1220
1221impl From<&CatalogItemId> for ObjectId {
1222    fn from(id: &CatalogItemId) -> Self {
1223        ObjectId::Item(*id)
1224    }
1225}
1226
1227impl From<CommentObjectId> for ObjectId {
1228    fn from(id: CommentObjectId) -> Self {
1229        match id {
1230            CommentObjectId::Table(item_id)
1231            | CommentObjectId::View(item_id)
1232            | CommentObjectId::MaterializedView(item_id)
1233            | CommentObjectId::Source(item_id)
1234            | CommentObjectId::Sink(item_id)
1235            | CommentObjectId::MetricSink(item_id)
1236            | CommentObjectId::Index(item_id)
1237            | CommentObjectId::Func(item_id)
1238            | CommentObjectId::Connection(item_id)
1239            | CommentObjectId::Type(item_id)
1240            | CommentObjectId::Secret(item_id) => ObjectId::Item(item_id),
1241            CommentObjectId::Role(id) => ObjectId::Role(id),
1242            CommentObjectId::Database(id) => ObjectId::Database(id),
1243            CommentObjectId::Schema(id) => ObjectId::Schema(id),
1244            CommentObjectId::Cluster(id) => ObjectId::Cluster(id),
1245            CommentObjectId::ClusterReplica(id) => ObjectId::ClusterReplica(id),
1246            CommentObjectId::NetworkPolicy(id) => ObjectId::NetworkPolicy(id),
1247        }
1248    }
1249}
1250
1251#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1252pub enum SystemObjectId {
1253    /// The ID of a specific object.
1254    Object(ObjectId),
1255    /// Identifier for the entire system.
1256    System,
1257}
1258
1259impl SystemObjectId {
1260    pub fn object_id(&self) -> Option<&ObjectId> {
1261        match self {
1262            SystemObjectId::Object(object_id) => Some(object_id),
1263            SystemObjectId::System => None,
1264        }
1265    }
1266
1267    pub fn is_system(&self) -> bool {
1268        matches!(self, SystemObjectId::System)
1269    }
1270}
1271
1272impl From<ObjectId> for SystemObjectId {
1273    fn from(id: ObjectId) -> Self {
1274        SystemObjectId::Object(id)
1275    }
1276}
1277
1278/// Comments can be applied to multiple kinds of objects (e.g. Tables and Role), so we need a way
1279/// to represent these different types and their IDs (e.g. [`CatalogItemId`] and [`RoleId`]), as
1280/// well as the inner kind of object that is represented, e.g. [`CatalogItemId`] is used to
1281/// identify both Tables and Views. No other kind of ID encapsulates all of this, hence this new
1282/// "*Id" type.
1283///
1284/// New variants here also need to be added to the durable counterpart
1285/// `mz_catalog_protos::objects::CommentObject` and to both CASE expressions in
1286/// the `mz_internal.mz_comments` materialized view (`MZ_COMMENTS` in
1287/// `mz-catalog::builtin::mz_internal`). The MV reads `CommentObject` out of
1288/// `mz_catalog_raw` as serde JSON.
1289#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
1290pub enum CommentObjectId {
1291    Table(CatalogItemId),
1292    View(CatalogItemId),
1293    MaterializedView(CatalogItemId),
1294    Source(CatalogItemId),
1295    Sink(CatalogItemId),
1296    MetricSink(CatalogItemId),
1297    Index(CatalogItemId),
1298    Func(CatalogItemId),
1299    Connection(CatalogItemId),
1300    Type(CatalogItemId),
1301    Secret(CatalogItemId),
1302    Role(RoleId),
1303    Database(DatabaseId),
1304    Schema((ResolvedDatabaseSpecifier, SchemaSpecifier)),
1305    Cluster(ClusterId),
1306    ClusterReplica((ClusterId, ReplicaId)),
1307    NetworkPolicy(NetworkPolicyId),
1308}
1309
1310/// Whether to resolve an name in the types namespace, the functions namespace,
1311/// or the relations namespace. It is possible to resolve name in multiple
1312/// namespaces, in which case types are preferred to functions are preferred to
1313/// relations.
1314// NOTE(benesch,sploiselle): The fact that some names are looked up in multiple
1315// namespaces is a bit dubious, and stems from the fact that we don't
1316// automatically create types for relations (see database-issues#7142). It's possible that we
1317// don't allow names to be looked up in multiple namespaces (i.e., this becomes
1318// `enum ItemResolutionNamespace`), but it's also possible that the design of
1319// the `DOC ON TYPE` option means we're forever stuck with this complexity.
1320#[derive(Debug, Clone, Copy)]
1321struct ItemResolutionConfig {
1322    types: bool,
1323    functions: bool,
1324    relations: bool,
1325}
1326
1327#[derive(Debug)]
1328pub struct NameResolver<'a> {
1329    catalog: &'a dyn SessionCatalog,
1330    ctes: BTreeMap<String, LocalId>,
1331    /// The next `LocalId` to allocate for a CTE. Never decremented, so every
1332    /// CTE in the statement gets a unique id, even when CTE names shadow each
1333    /// other. Later phases (e.g., HIR lowering's `CteMap`) key CTEs by
1334    /// `LocalId` and rely on this uniqueness.
1335    next_cte_id: u64,
1336    status: Result<(), PlanError>,
1337    ids: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>,
1338}
1339
1340impl<'a> NameResolver<'a> {
1341    fn new(catalog: &'a dyn SessionCatalog) -> NameResolver<'a> {
1342        NameResolver {
1343            catalog,
1344            ctes: BTreeMap::new(),
1345            next_cte_id: 0,
1346            status: Ok(()),
1347            ids: BTreeMap::new(),
1348        }
1349    }
1350
1351    fn allocate_cte_id(&mut self) -> LocalId {
1352        let id = LocalId::new(self.next_cte_id);
1353        self.next_cte_id += 1;
1354        id
1355    }
1356
1357    fn resolve_data_type(&mut self, data_type: RawDataType) -> Result<ResolvedDataType, PlanError> {
1358        self.resolve_data_type_inner(data_type, true)
1359    }
1360
1361    fn resolve_data_type_inner(
1362        &mut self,
1363        data_type: RawDataType,
1364        should_insert_id: bool,
1365    ) -> Result<ResolvedDataType, PlanError> {
1366        match data_type {
1367            RawDataType::Array(elem_type) => {
1368                let name = elem_type.to_string();
1369                // `T[]` recursively resolves to its element type, but we
1370                // only want to insert its array type `_T` and not its element
1371                // type `T`. Thus we set `should_insert_id` to `false`.
1372                match self.resolve_data_type_inner(*elem_type, false)? {
1373                    ResolvedDataType::AnonymousList(_) | ResolvedDataType::AnonymousMap { .. } => {
1374                        sql_bail!("type \"{}[]\" does not exist", name)
1375                    }
1376                    ResolvedDataType::Named { id, modifiers, .. } => {
1377                        let element_item = self.catalog.get_item(&id);
1378                        let array_item = match element_item.type_details() {
1379                            Some(CatalogTypeDetails {
1380                                array_id: Some(array_id),
1381                                ..
1382                            }) => self.catalog.get_item(array_id),
1383                            Some(_) => sql_bail!("type \"{}[]\" does not exist", name),
1384                            None => {
1385                                // Resolution should never produce a
1386                                // `ResolvedDataType::Named` with an ID of a
1387                                // non-type, but we error gracefully just in
1388                                // case.
1389                                sql_bail!(
1390                                    "internal error: {} does not refer to a type",
1391                                    self.catalog
1392                                        .resolve_full_name(element_item.name())
1393                                        .to_string()
1394                                        .quoted()
1395                                );
1396                            }
1397                        };
1398
1399                        // We don't need to check `should_insert_id` here since we're
1400                        // guaranteed to insert just one array item type for an
1401                        // array. For multi-dimensional arrays, the parser always
1402                        // collapses it into a 1D array.
1403                        self.ids.insert(array_item.id(), BTreeSet::new());
1404                        Ok(ResolvedDataType::Named {
1405                            id: array_item.id(),
1406                            qualifiers: array_item.name().qualifiers.clone(),
1407                            full_name: self.catalog.resolve_full_name(array_item.name()),
1408                            modifiers,
1409                            print_id: true,
1410                        })
1411                    }
1412                    ResolvedDataType::Error => sql_bail!("type \"{}[]\" does not exist", name),
1413                }
1414            }
1415            RawDataType::List(elem_type) => {
1416                let elem_type = self.resolve_data_type_inner(*elem_type, should_insert_id)?;
1417                Ok(ResolvedDataType::AnonymousList(Box::new(elem_type)))
1418            }
1419            RawDataType::Map {
1420                key_type,
1421                value_type,
1422            } => {
1423                let key_type = self.resolve_data_type_inner(*key_type, should_insert_id)?;
1424                let value_type = self.resolve_data_type_inner(*value_type, should_insert_id)?;
1425                Ok(ResolvedDataType::AnonymousMap {
1426                    key_type: Box::new(key_type),
1427                    value_type: Box::new(value_type),
1428                })
1429            }
1430            RawDataType::Other { name, typ_mod } => {
1431                let (full_name, item) = match name {
1432                    RawItemName::Name(name) => {
1433                        let name = normalize::unresolved_item_name(name)?;
1434                        let item = self.catalog.resolve_type(&name)?;
1435                        let full_name = self.catalog.resolve_full_name(item.name());
1436                        (full_name, item)
1437                    }
1438                    RawItemName::Id(id, name, version) => {
1439                        let id: CatalogItemId = id.parse()?;
1440                        let item = match self.catalog.try_get_item(&id) {
1441                            Some(item) => item,
1442                            None => return Err(PlanError::InvalidId(id)),
1443                        };
1444                        let full_name = normalize::full_name(name)?;
1445                        if version.is_some() {
1446                            sql_bail!("specifying a version for a type reference is not supported");
1447                        }
1448
1449                        (full_name, item)
1450                    }
1451                };
1452                if should_insert_id {
1453                    self.ids.insert(item.id(), BTreeSet::new());
1454                }
1455                Ok(ResolvedDataType::Named {
1456                    id: item.id(),
1457                    qualifiers: item.name().qualifiers.clone(),
1458                    full_name,
1459                    modifiers: typ_mod,
1460                    print_id: true,
1461                })
1462            }
1463        }
1464    }
1465
1466    fn resolve_item_name(
1467        &mut self,
1468        item_name: RawItemName,
1469        config: ItemResolutionConfig,
1470    ) -> ResolvedItemName {
1471        match item_name {
1472            RawItemName::Name(name) => self.resolve_item_name_name(name, config),
1473            RawItemName::Id(id, raw_name, version) => {
1474                self.resolve_item_name_id(id, raw_name, version)
1475            }
1476        }
1477    }
1478
1479    fn resolve_item_name_name(
1480        &mut self,
1481        raw_name: UnresolvedItemName,
1482        config: ItemResolutionConfig,
1483    ) -> ResolvedItemName {
1484        let raw_name = match normalize::unresolved_item_name(raw_name) {
1485            Ok(raw_name) => raw_name,
1486            Err(e) => {
1487                if self.status.is_ok() {
1488                    self.status = Err(e);
1489                }
1490                return ResolvedItemName::Error;
1491            }
1492        };
1493
1494        let mut r: Result<&dyn CatalogItem, CatalogError> =
1495            Err(CatalogError::UnknownItem(raw_name.to_string()));
1496
1497        if r.is_err() && config.types {
1498            r = self.catalog.resolve_type(&raw_name);
1499        }
1500
1501        if r.is_err() && config.functions {
1502            r = self.catalog.resolve_function(&raw_name);
1503        }
1504
1505        if r.is_err() && config.relations {
1506            // Check if unqualified name refers to a CTE.
1507            //
1508            // Note that this is done in non-function contexts as CTEs
1509            // are treated as relations.
1510            if raw_name.database.is_none() && raw_name.schema.is_none() {
1511                let norm_name = normalize::ident(Ident::new_unchecked(&raw_name.item));
1512                if let Some(id) = self.ctes.get(&norm_name) {
1513                    return ResolvedItemName::Cte {
1514                        id: *id,
1515                        name: norm_name,
1516                    };
1517                }
1518            }
1519            r = self.catalog.resolve_item(&raw_name);
1520        };
1521
1522        match r {
1523            Ok(item) => {
1524                // Record the item at its current version.
1525                let item = item.at_version(RelationVersionSelector::Latest);
1526                self.ids
1527                    .entry(item.id())
1528                    .or_default()
1529                    .insert(item.global_id());
1530                let print_id = !matches!(
1531                    item.item_type(),
1532                    CatalogItemType::Func | CatalogItemType::Type
1533                );
1534                let alter_table_enabled =
1535                    self.catalog.system_vars().enable_alter_table_add_column();
1536                let version = match item.latest_version() {
1537                    // Only track the version of referenced object if the feature is enabled.
1538                    Some(v) if item.id().is_user() && alter_table_enabled => {
1539                        RelationVersionSelector::Specific(v)
1540                    }
1541                    _ => RelationVersionSelector::Latest,
1542                };
1543
1544                ResolvedItemName::Item {
1545                    id: item.id(),
1546                    qualifiers: item.name().qualifiers.clone(),
1547                    full_name: self.catalog.resolve_full_name(item.name()),
1548                    print_id,
1549                    version,
1550                }
1551            }
1552            Err(mut e) => {
1553                if self.status.is_ok() {
1554                    match &mut e {
1555                        CatalogError::UnknownFunction {
1556                            name: _,
1557                            alternative,
1558                        } => {
1559                            // Suggest using the `jsonb_` version of `json_`
1560                            // functions that do not exist.
1561                            if raw_name.database.is_none()
1562                                && (raw_name.schema.is_none()
1563                                    || raw_name.schema.as_deref() == Some("pg_catalog")
1564                                        && raw_name.item.starts_with("json_"))
1565                            {
1566                                let jsonb_name = PartialItemName {
1567                                    item: raw_name.item.replace("json_", "jsonb_"),
1568                                    ..raw_name
1569                                };
1570                                if self.catalog.resolve_function(&jsonb_name).is_ok() {
1571                                    *alternative = Some(jsonb_name.to_string());
1572                                }
1573                            }
1574                        }
1575                        _ => (),
1576                    }
1577
1578                    self.status = Err(e.into());
1579                }
1580                ResolvedItemName::Error
1581            }
1582        }
1583    }
1584
1585    fn resolve_item_name_id(
1586        &mut self,
1587        id: String,
1588        raw_name: UnresolvedItemName,
1589        version: Option<Version>,
1590    ) -> ResolvedItemName {
1591        let id: CatalogItemId = match id.parse() {
1592            Ok(id) => id,
1593            Err(e) => {
1594                if self.status.is_ok() {
1595                    self.status = Err(e.into());
1596                }
1597                return ResolvedItemName::Error;
1598            }
1599        };
1600        let item = match self.catalog.try_get_item(&id) {
1601            Some(item) => item,
1602            None => {
1603                if self.status.is_ok() {
1604                    self.status = Err(PlanError::InvalidId(id));
1605                }
1606                return ResolvedItemName::Error;
1607            }
1608        };
1609        let alter_table_enabled = self.catalog.system_vars().enable_alter_table_add_column();
1610        let version = match version {
1611            // If there isn't a version specified, and this item supports versioning, track the
1612            // latest.
1613            None => match item.latest_version() {
1614                // Only pin a version for user items, and only with the feature on. Mirrors the
1615                // by-name path in `fold_item_name`. Builtins are not user-versioned, so pinning
1616                // one strands the reference if the builtin is ever converted to an item type
1617                // without versions.
1618                Some(v) if id.is_user() && alter_table_enabled => {
1619                    RelationVersionSelector::Specific(v)
1620                }
1621                _ => RelationVersionSelector::Latest,
1622            },
1623            // Note: Return the specific version if one is specified, even if the feature is off.
1624            Some(v) => {
1625                let specified_version = RelationVersion::from(v);
1626                match item.latest_version() {
1627                    Some(latest) if latest >= specified_version => {
1628                        RelationVersionSelector::Specific(specified_version)
1629                    }
1630                    // A version pin on a builtin is meaningless, since builtins are not
1631                    // user-versioned. Such a pin can still sit in a persisted catalog, and if the
1632                    // builtin has been converted to an item type without versions it no longer
1633                    // validates. Resolve to latest instead of failing catalog open. User items
1634                    // keep the strict check so real out-of-range versions still error.
1635                    _ if !id.is_user() => RelationVersionSelector::Latest,
1636                    _ => {
1637                        if self.status.is_ok() {
1638                            self.status = Err(PlanError::InvalidVersion {
1639                                name: item.name().item.clone(),
1640                                version: v.to_string(),
1641                            })
1642                        }
1643                        return ResolvedItemName::Error;
1644                    }
1645                }
1646            }
1647        };
1648        let item = item.at_version(version);
1649        self.ids
1650            .entry(item.id())
1651            .or_default()
1652            .insert(item.global_id());
1653
1654        let full_name = match normalize::full_name(raw_name) {
1655            Ok(full_name) => full_name,
1656            Err(e) => {
1657                if self.status.is_ok() {
1658                    self.status = Err(e);
1659                }
1660                return ResolvedItemName::Error;
1661            }
1662        };
1663        ResolvedItemName::Item {
1664            id,
1665            qualifiers: item.name().qualifiers.clone(),
1666            full_name,
1667            print_id: true,
1668            version,
1669        }
1670    }
1671
1672    /// Resolves an item name in a `DOC ON` position (`DOC ON TYPE x`,
1673    /// `DOC ON COLUMN x.c`), where the name may denote a type or a relation. Also used for `COMMENT
1674    /// ON COLUMN`.
1675    fn resolve_doc_on_name(&mut self, name: RawItemName) -> ResolvedItemName {
1676        let mut name = self.resolve_item_name(
1677            name,
1678            // The name can refer to either a type or a relation.
1679            //
1680            // It's possible this will get simpler once database-issues#7142 is
1681            // fixed. See the comment on `ItemResolutionConfig` for details.
1682            ItemResolutionConfig {
1683                functions: false,
1684                types: true,
1685                relations: true,
1686            },
1687        );
1688        if let ResolvedItemName::Item { print_id, .. } = &mut name {
1689            // These references persist in a sink's `create_sql`, so the resolved name must print
1690            // its id, otherwise it can't recover it when recreating
1691            *print_id = true;
1692        }
1693        name
1694    }
1695}
1696
1697impl<'a> Fold<Raw, Aug> for NameResolver<'a> {
1698    fn fold_nested_statement(
1699        &mut self,
1700        stmt: <Raw as AstInfo>::NestedStatement,
1701    ) -> <Aug as AstInfo>::NestedStatement {
1702        stmt
1703    }
1704
1705    fn fold_query(&mut self, q: Query<Raw>) -> Query<Aug> {
1706        // Retain the old values of various CTE names so that we can restore them after we're done
1707        // planning this SELECT.
1708        let mut shadowed_cte_ids = Vec::new();
1709
1710        // A reused identifier indicates a reused name.
1711        use itertools::Itertools;
1712        if let Some(ident) = q.ctes.bound_identifiers().duplicates().next() {
1713            self.status = Err(sql_err!(
1714                "WITH query name \"{}\" specified more than once",
1715                normalize::ident_ref(ident),
1716            ));
1717        }
1718
1719        let ctes: CteBlock<Aug> = match q.ctes {
1720            CteBlock::Simple(ctes) => {
1721                let mut result_ctes = Vec::<Cte<Aug>>::new();
1722
1723                for cte in ctes.into_iter() {
1724                    let cte_name = normalize::ident(cte.alias.name.clone());
1725                    let local_id = self.allocate_cte_id();
1726
1727                    result_ctes.push(Cte {
1728                        alias: cte.alias,
1729                        id: local_id,
1730                        query: self.fold_query(cte.query),
1731                    });
1732
1733                    let shadowed_id = self.ctes.insert(cte_name.clone(), local_id);
1734                    shadowed_cte_ids.push((cte_name, shadowed_id));
1735                }
1736                CteBlock::Simple(result_ctes)
1737            }
1738            CteBlock::MutuallyRecursive(MutRecBlock { options, ctes }) => {
1739                let mut result_ctes = Vec::<CteMutRec<Aug>>::new();
1740
1741                // All bindings go into scope before any definition is walked,
1742                // so that the definitions can refer to each other.
1743                let mut local_ids = Vec::with_capacity(ctes.len());
1744                for cte in ctes.iter() {
1745                    let cte_name = normalize::ident(cte.name.clone());
1746                    let local_id = self.allocate_cte_id();
1747                    let shadowed_id = self.ctes.insert(cte_name.clone(), local_id);
1748                    shadowed_cte_ids.push((cte_name, shadowed_id));
1749                    local_ids.push(local_id);
1750                }
1751
1752                for (cte, local_id) in ctes.into_iter().zip_eq(local_ids) {
1753                    let columns = cte
1754                        .columns
1755                        .into_iter()
1756                        .map(|column| self.fold_cte_mut_rec_column_def(column))
1757                        .collect();
1758                    let query = self.fold_query(cte.query);
1759                    result_ctes.push(CteMutRec {
1760                        name: cte.name,
1761                        columns,
1762                        id: local_id,
1763                        query,
1764                    });
1765                }
1766                CteBlock::MutuallyRecursive(MutRecBlock {
1767                    options: options
1768                        .into_iter()
1769                        .map(|option| self.fold_mut_rec_block_option(option))
1770                        .collect(),
1771                    ctes: result_ctes,
1772                })
1773            }
1774        };
1775
1776        let result = Query {
1777            ctes,
1778            // Queries can be recursive, so need the ability to grow the stack.
1779            body: mz_ore::stack::maybe_grow(|| self.fold_set_expr(q.body)),
1780            limit: q.limit.map(|l| self.fold_limit(l)),
1781            offset: q.offset.map(|l| self.fold_expr(l)),
1782            order_by: q
1783                .order_by
1784                .into_iter()
1785                .map(|c| self.fold_order_by_expr(c))
1786                .collect(),
1787        };
1788
1789        // Restore the old values of the CTEs.
1790        for (name, value) in shadowed_cte_ids.iter() {
1791            match value {
1792                Some(value) => {
1793                    self.ctes.insert(name.to_string(), value.clone());
1794                }
1795                None => {
1796                    self.ctes.remove(name);
1797                }
1798            };
1799        }
1800
1801        result
1802    }
1803
1804    fn fold_cte_id(&mut self, _id: <Raw as AstInfo>::CteId) -> <Aug as AstInfo>::CteId {
1805        panic!("this should have been handled when walking the CTE");
1806    }
1807
1808    fn fold_item_name(
1809        &mut self,
1810        item_name: <Raw as AstInfo>::ItemName,
1811    ) -> <Aug as AstInfo>::ItemName {
1812        self.resolve_item_name(
1813            item_name,
1814            // By default, when resolving an item name, we assume only relations
1815            // should be in scope.
1816            ItemResolutionConfig {
1817                functions: false,
1818                types: false,
1819                relations: true,
1820            },
1821        )
1822    }
1823
1824    fn fold_column_name(&mut self, column_name: ast::ColumnName<Raw>) -> ast::ColumnName<Aug> {
1825        let item_name = self.resolve_doc_on_name(column_name.relation);
1826
1827        match &item_name {
1828            ResolvedItemName::Item {
1829                id,
1830                full_name,
1831                version,
1832                qualifiers: _,
1833                print_id: _,
1834            } => {
1835                let item = self.catalog.get_item(id).at_version(*version);
1836                let name = normalize::column_name(column_name.column.clone());
1837
1838                let maybe_desc = match item.type_details() {
1839                    Some(details) => match details.typ.desc(self.catalog) {
1840                        Ok(desc) => desc.map(Cow::Owned),
1841                        Err(e) => {
1842                            if self.status.is_ok() {
1843                                self.status = Err(e);
1844                            }
1845                            return ast::ColumnName {
1846                                relation: ResolvedItemName::Error,
1847                                column: ResolvedColumnReference::Error,
1848                            };
1849                        }
1850                    },
1851                    None => item.relation_desc(),
1852                };
1853                let Some(desc) = maybe_desc else {
1854                    if self.status.is_ok() {
1855                        self.status = Err(PlanError::ItemWithoutColumns {
1856                            name: full_name.to_string(),
1857                            item_type: item.item_type(),
1858                        });
1859                    }
1860                    return ast::ColumnName {
1861                        relation: ResolvedItemName::Error,
1862                        column: ResolvedColumnReference::Error,
1863                    };
1864                };
1865
1866                let Some((index, _typ)) = desc.get_by_name(&name) else {
1867                    if self.status.is_ok() {
1868                        let similar = desc.iter_similar_names(&name).cloned().collect();
1869                        self.status = Err(PlanError::UnknownColumn {
1870                            table: Some(full_name.clone().into()),
1871                            column: name,
1872                            similar,
1873                        })
1874                    }
1875                    return ast::ColumnName {
1876                        relation: ResolvedItemName::Error,
1877                        column: ResolvedColumnReference::Error,
1878                    };
1879                };
1880
1881                ast::ColumnName {
1882                    relation: item_name,
1883                    column: ResolvedColumnReference::Column { name, index },
1884                }
1885            }
1886            ResolvedItemName::Cte { .. } | ResolvedItemName::Error => ast::ColumnName {
1887                relation: ResolvedItemName::Error,
1888                column: ResolvedColumnReference::Error,
1889            },
1890        }
1891    }
1892
1893    fn fold_column_reference(
1894        &mut self,
1895        _node: <Raw as AstInfo>::ColumnReference,
1896    ) -> <Aug as AstInfo>::ColumnReference {
1897        // Do not call this function directly; instead resolve through `fold_column_name`
1898        ResolvedColumnReference::Error
1899    }
1900
1901    fn fold_data_type(
1902        &mut self,
1903        data_type: <Raw as AstInfo>::DataType,
1904    ) -> <Aug as AstInfo>::DataType {
1905        match self.resolve_data_type(data_type) {
1906            Ok(data_type) => data_type,
1907            Err(e) => {
1908                if self.status.is_ok() {
1909                    self.status = Err(e);
1910                }
1911                ResolvedDataType::Error
1912            }
1913        }
1914    }
1915
1916    fn fold_schema_name(
1917        &mut self,
1918        name: <Raw as AstInfo>::SchemaName,
1919    ) -> <Aug as AstInfo>::SchemaName {
1920        let norm_name = match normalize::unresolved_schema_name(name) {
1921            Ok(norm_name) => norm_name,
1922            Err(e) => {
1923                if self.status.is_ok() {
1924                    self.status = Err(e);
1925                }
1926                return ResolvedSchemaName::Error;
1927            }
1928        };
1929
1930        // Special case for mz_temp: with lazy temporary schema creation, the temp
1931        // schema may not exist yet. Return a resolved name with SchemaSpecifier::Temporary
1932        // so that downstream code can handle it appropriately (e.g., return a proper error).
1933        if norm_name.database.is_none() && norm_name.schema == mz_repr::namespaces::MZ_TEMP_SCHEMA {
1934            return ResolvedSchemaName::Schema {
1935                database_spec: ResolvedDatabaseSpecifier::Ambient,
1936                schema_spec: SchemaSpecifier::Temporary,
1937                full_name: FullSchemaName {
1938                    database: RawDatabaseSpecifier::Ambient,
1939                    schema: mz_repr::namespaces::MZ_TEMP_SCHEMA.to_string(),
1940                },
1941            };
1942        }
1943
1944        match self
1945            .catalog
1946            .resolve_schema(norm_name.database.as_deref(), norm_name.schema.as_str())
1947        {
1948            Ok(schema) => {
1949                let raw_database_spec = match schema.database() {
1950                    ResolvedDatabaseSpecifier::Ambient => RawDatabaseSpecifier::Ambient,
1951                    ResolvedDatabaseSpecifier::Id(id) => {
1952                        RawDatabaseSpecifier::Name(self.catalog.get_database(id).name().to_string())
1953                    }
1954                };
1955                ResolvedSchemaName::Schema {
1956                    database_spec: schema.database().clone(),
1957                    schema_spec: schema.id().clone(),
1958                    full_name: FullSchemaName {
1959                        database: raw_database_spec,
1960                        schema: schema.name().schema.clone(),
1961                    },
1962                }
1963            }
1964            Err(e) => {
1965                if self.status.is_ok() {
1966                    self.status = Err(e.into());
1967                }
1968                ResolvedSchemaName::Error
1969            }
1970        }
1971    }
1972
1973    fn fold_database_name(
1974        &mut self,
1975        database_name: <Raw as AstInfo>::DatabaseName,
1976    ) -> <Aug as AstInfo>::DatabaseName {
1977        match self.catalog.resolve_database(database_name.0.as_str()) {
1978            Ok(database) => ResolvedDatabaseName::Database {
1979                id: database.id(),
1980                name: database_name.0.into_string(),
1981            },
1982            Err(e) => {
1983                if self.status.is_ok() {
1984                    self.status = Err(e.into());
1985                }
1986                ResolvedDatabaseName::Error
1987            }
1988        }
1989    }
1990
1991    fn fold_cluster_name(
1992        &mut self,
1993        cluster_name: <Raw as AstInfo>::ClusterName,
1994    ) -> <Aug as AstInfo>::ClusterName {
1995        match cluster_name {
1996            RawClusterName::Unresolved(ident) => {
1997                match self.catalog.resolve_cluster(Some(ident.as_str())) {
1998                    Ok(cluster) => ResolvedClusterName {
1999                        id: cluster.id(),
2000                        print_name: None,
2001                    },
2002                    Err(e) => {
2003                        self.status = Err(e.into());
2004                        ResolvedClusterName {
2005                            // The ID is arbitrary here; we just need some dummy
2006                            // value to return.
2007                            id: ClusterId::system(0).expect("0 is a valid ID"),
2008                            print_name: None,
2009                        }
2010                    }
2011                }
2012            }
2013            RawClusterName::Resolved(ident) => match ident.parse() {
2014                Ok(id) => ResolvedClusterName {
2015                    id,
2016                    print_name: None,
2017                },
2018                Err(e) => {
2019                    self.status = Err(e.into());
2020                    ResolvedClusterName {
2021                        // The ID is arbitrary here; we just need some dummy
2022                        // value to return.
2023                        id: ClusterId::system(0).expect("0 is a valid ID"),
2024                        print_name: None,
2025                    }
2026                }
2027            },
2028        }
2029    }
2030
2031    fn fold_with_option_value(
2032        &mut self,
2033        node: mz_sql_parser::ast::WithOptionValue<Raw>,
2034    ) -> mz_sql_parser::ast::WithOptionValue<Aug> {
2035        use mz_sql_parser::ast::WithOptionValue::*;
2036        match node {
2037            Sequence(vs) => Sequence(
2038                vs.into_iter()
2039                    .map(|v| self.fold_with_option_value(v))
2040                    .collect(),
2041            ),
2042            Map(map) => Map(map
2043                .into_iter()
2044                .map(|(k, v)| (k, self.fold_with_option_value(v)))
2045                .collect()),
2046            Value(v) => Value(self.fold_value(v)),
2047            DataType(dt) => DataType(self.fold_data_type(dt)),
2048            Secret(secret) => {
2049                let item_name = self.fold_item_name(secret);
2050                match &item_name {
2051                    ResolvedItemName::Item { id, .. } => {
2052                        let item = self.catalog.get_item(id);
2053                        if item.item_type() != CatalogItemType::Secret {
2054                            self.status =
2055                                Err(PlanError::InvalidSecret(Box::new(item_name.clone())));
2056                        }
2057                    }
2058                    ResolvedItemName::Cte { .. } => {
2059                        self.status = Err(PlanError::InvalidSecret(Box::new(item_name.clone())));
2060                    }
2061                    ResolvedItemName::Error => {}
2062                }
2063                Secret(item_name)
2064            }
2065            Item(obj) => {
2066                let item_name = self.fold_item_name(obj);
2067                match &item_name {
2068                    ResolvedItemName::Item { .. } => {}
2069                    ResolvedItemName::Cte { .. } => {
2070                        self.status = Err(PlanError::InvalidObject(Box::new(item_name.clone())));
2071                    }
2072                    ResolvedItemName::Error => {}
2073                }
2074                Item(item_name)
2075            }
2076            UnresolvedItemName(name) => UnresolvedItemName(self.fold_unresolved_item_name(name)),
2077            Ident(name) => Ident(self.fold_ident(name)),
2078            Expr(e) => Expr(self.fold_expr(e)),
2079            ClusterReplicas(replicas) => ClusterReplicas(
2080                replicas
2081                    .into_iter()
2082                    .map(|r| self.fold_replica_definition(r))
2083                    .collect(),
2084            ),
2085            ConnectionKafkaBroker(broker) => ConnectionKafkaBroker(self.fold_kafka_broker(broker)),
2086            ConnectionAwsPrivatelink(privatelink) => {
2087                ConnectionAwsPrivatelink(self.fold_connection_default_aws_privatelink(privatelink))
2088            }
2089            KafkaMatchingBrokerRule(x) => {
2090                KafkaMatchingBrokerRule(self.fold_kafka_matching_broker_rule(x))
2091            }
2092            RetainHistoryFor(value) => RetainHistoryFor(self.fold_value(value)),
2093            Refresh(refresh) => Refresh(self.fold_refresh_option_value(refresh)),
2094            ClusterScheduleOptionValue(value) => ClusterScheduleOptionValue(value),
2095            ClusterAutoScalingStrategyOptionValue(value) => {
2096                ClusterAutoScalingStrategyOptionValue(value)
2097            }
2098            ClusterAlterStrategy(value) => {
2099                ClusterAlterStrategy(self.fold_cluster_alter_option_value(value))
2100            }
2101            NetworkPolicyRules(rules) => NetworkPolicyRules(
2102                rules
2103                    .into_iter()
2104                    .map(|r| self.fold_network_policy_rule_definition(r))
2105                    .collect(),
2106            ),
2107        }
2108    }
2109
2110    fn fold_role_name(&mut self, name: <Raw as AstInfo>::RoleName) -> <Aug as AstInfo>::RoleName {
2111        match self.catalog.resolve_role(name.as_str()) {
2112            Ok(role) => ResolvedRoleName {
2113                id: role.id(),
2114                name: role.name().to_string(),
2115            },
2116            Err(e) => {
2117                if self.status.is_ok() {
2118                    self.status = Err(e.into());
2119                }
2120                // garbage value that will be ignored since there's an error.
2121                ResolvedRoleName {
2122                    id: RoleId::User(0),
2123                    name: "".to_string(),
2124                }
2125            }
2126        }
2127    }
2128
2129    fn fold_network_policy_name(
2130        &mut self,
2131        name: <Raw as AstInfo>::NetworkPolicyName,
2132    ) -> <Aug as AstInfo>::NetworkPolicyName {
2133        let name_str = match &name {
2134            RawNetworkPolicyName::Unresolved(ident) => ident.as_str(),
2135            RawNetworkPolicyName::Resolved(s) => s.as_str(),
2136        };
2137        match self.catalog.resolve_network_policy(name_str) {
2138            Ok(policy) => ResolvedNetworkPolicyName {
2139                id: policy.id(),
2140                name: policy.name().to_string(),
2141            },
2142            Err(e) => {
2143                if self.status.is_ok() {
2144                    self.status = Err(e.into());
2145                }
2146                // garbage value that will be ignored since there's an error.
2147                ResolvedNetworkPolicyName {
2148                    id: NetworkPolicyId::User(0),
2149                    name: "".to_string(),
2150                }
2151            }
2152        }
2153    }
2154
2155    fn fold_object_name(
2156        &mut self,
2157        name: <Raw as AstInfo>::ObjectName,
2158    ) -> <Aug as AstInfo>::ObjectName {
2159        match name {
2160            UnresolvedObjectName::Cluster(name) => ResolvedObjectName::Cluster(
2161                self.fold_cluster_name(RawClusterName::Unresolved(name)),
2162            ),
2163            UnresolvedObjectName::ClusterReplica(name) => {
2164                match self.catalog.resolve_cluster_replica(&name) {
2165                    Ok(cluster_replica) => {
2166                        ResolvedObjectName::ClusterReplica(ResolvedClusterReplicaName {
2167                            cluster_id: cluster_replica.cluster_id(),
2168                            replica_id: cluster_replica.replica_id(),
2169                        })
2170                    }
2171                    Err(e) => {
2172                        self.status = Err(e.into());
2173                        ResolvedObjectName::ClusterReplica(ResolvedClusterReplicaName {
2174                            // The ID is arbitrary here; we just need some dummy
2175                            // value to return.
2176                            cluster_id: ClusterId::system(0).expect("0 is a valid ID"),
2177                            replica_id: ReplicaId::System(0),
2178                        })
2179                    }
2180                }
2181            }
2182            UnresolvedObjectName::Database(name) => {
2183                ResolvedObjectName::Database(self.fold_database_name(name))
2184            }
2185            UnresolvedObjectName::Schema(name) => {
2186                ResolvedObjectName::Schema(self.fold_schema_name(name))
2187            }
2188            UnresolvedObjectName::Role(name) => ResolvedObjectName::Role(self.fold_role_name(name)),
2189            UnresolvedObjectName::Item(name) => {
2190                ResolvedObjectName::Item(self.fold_item_name(RawItemName::Name(name)))
2191            }
2192            UnresolvedObjectName::NetworkPolicy(name) => ResolvedObjectName::NetworkPolicy(
2193                self.fold_network_policy_name(RawNetworkPolicyName::Unresolved(name)),
2194            ),
2195        }
2196    }
2197
2198    fn fold_function(
2199        &mut self,
2200        node: mz_sql_parser::ast::Function<Raw>,
2201    ) -> mz_sql_parser::ast::Function<Aug> {
2202        // Functions implemented as SQL statements can have very deeply nested
2203        // and recursive structures, so need the ability to grow the stack.
2204        mz_ore::stack::maybe_grow(|| {
2205            mz_sql_parser::ast::Function {
2206                name: self.resolve_item_name(
2207                    node.name,
2208                    // When resolving a function name, only function items should be
2209                    // considered.
2210                    ItemResolutionConfig {
2211                        functions: true,
2212                        types: false,
2213                        relations: false,
2214                    },
2215                ),
2216                args: self.fold_function_args(node.args),
2217                filter: node.filter.map(|expr| Box::new(self.fold_expr(*expr))),
2218                over: node.over.map(|over| self.fold_window_spec(over)),
2219                distinct: node.distinct,
2220            }
2221        })
2222    }
2223
2224    fn fold_table_factor(
2225        &mut self,
2226        node: mz_sql_parser::ast::TableFactor<Raw>,
2227    ) -> mz_sql_parser::ast::TableFactor<Aug> {
2228        use mz_sql_parser::ast::TableFactor::*;
2229        match node {
2230            Table { name, alias } => Table {
2231                name: self.fold_item_name(name),
2232                alias: alias.map(|alias| self.fold_table_alias(alias)),
2233            },
2234            Function {
2235                function,
2236                alias,
2237                with_ordinality,
2238            } => {
2239                match &function.name {
2240                    RawItemName::Name(name) => {
2241                        if *name == UnresolvedItemName::unqualified(ident!("values"))
2242                            && self.status.is_ok()
2243                        {
2244                            self.status = Err(PlanError::FromValueRequiresParen);
2245                        }
2246                    }
2247                    RawItemName::Id(..) => {}
2248                }
2249
2250                Function {
2251                    function: self.fold_function(function),
2252                    alias: alias.map(|alias| self.fold_table_alias(alias)),
2253                    with_ordinality,
2254                }
2255            }
2256            RowsFrom {
2257                functions,
2258                alias,
2259                with_ordinality,
2260            } => RowsFrom {
2261                functions: functions
2262                    .into_iter()
2263                    .map(|f| self.fold_function(f))
2264                    .collect(),
2265                alias: alias.map(|alias| self.fold_table_alias(alias)),
2266                with_ordinality,
2267            },
2268            Derived {
2269                lateral,
2270                subquery,
2271                alias,
2272            } => Derived {
2273                lateral,
2274                subquery: Box::new(self.fold_query(*subquery)),
2275                alias: alias.map(|alias| self.fold_table_alias(alias)),
2276            },
2277            NestedJoin { join, alias } => NestedJoin {
2278                join: Box::new(self.fold_table_with_joins(*join)),
2279                alias: alias.map(|alias| self.fold_table_alias(alias)),
2280            },
2281        }
2282    }
2283
2284    fn fold_grant_target_specification(
2285        &mut self,
2286        node: GrantTargetSpecification<Raw>,
2287    ) -> GrantTargetSpecification<Aug> {
2288        match node {
2289            GrantTargetSpecification::Object {
2290                object_type: ObjectType::Type,
2291                object_spec_inner: GrantTargetSpecificationInner::Objects { names },
2292            } => GrantTargetSpecification::Object {
2293                object_type: ObjectType::Type,
2294                object_spec_inner: GrantTargetSpecificationInner::Objects {
2295                    names: names
2296                        .into_iter()
2297                        .map(|name| match name {
2298                            UnresolvedObjectName::Item(name) => {
2299                                ResolvedObjectName::Item(self.resolve_item_name_name(
2300                                    name,
2301                                    // `{GRANT|REVOKE} ... ON TYPE ...` can only
2302                                    // refer to type names.
2303                                    ItemResolutionConfig {
2304                                        functions: false,
2305                                        types: true,
2306                                        relations: false,
2307                                    },
2308                                ))
2309                            }
2310                            _ => self.fold_object_name(name),
2311                        })
2312                        .collect(),
2313                },
2314            },
2315            _ => mz_sql_parser::ast::fold::fold_grant_target_specification(self, node),
2316        }
2317    }
2318
2319    fn fold_doc_on_identifier(&mut self, node: DocOnIdentifier<Raw>) -> DocOnIdentifier<Aug> {
2320        match node {
2321            DocOnIdentifier::Column(name) => DocOnIdentifier::Column(self.fold_column_name(name)),
2322            DocOnIdentifier::Type(name) => DocOnIdentifier::Type(self.resolve_doc_on_name(name)),
2323        }
2324    }
2325
2326    fn fold_expr(&mut self, node: Expr<Raw>) -> Expr<Aug> {
2327        // Exprs can be recursive, so need the ability to grow the stack.
2328        mz_ore::stack::maybe_grow(|| mz_sql_parser::ast::fold::fold_expr(self, node))
2329    }
2330}
2331
2332/// Resolves names in an AST node using the provided catalog.
2333#[mz_ore::instrument(target = "compiler", level = "trace", name = "ast_resolve_names")]
2334pub fn resolve<N>(
2335    catalog: &dyn SessionCatalog,
2336    node: N,
2337) -> Result<(N::Folded, ResolvedIds), PlanError>
2338where
2339    N: FoldNode<Raw, Aug>,
2340{
2341    let mut resolver = NameResolver::new(catalog);
2342    let result = node.fold(&mut resolver);
2343    resolver.status?;
2344    Ok((result, ResolvedIds::new(resolver.ids)))
2345}
2346
2347/// A set of items and their corresponding collections resolved by name resolution.
2348///
2349/// This is a newtype of a [`BTreeMap`] that is provided to make it harder to confuse a set of
2350/// resolved IDs with other collections of [`CatalogItemId`].
2351#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2352pub struct ResolvedIds {
2353    #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
2354    entries: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>,
2355}
2356
2357impl ResolvedIds {
2358    fn new(entries: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>) -> Self {
2359        ResolvedIds { entries }
2360    }
2361
2362    /// Returns an emptry [`ResolvedIds`].
2363    pub fn empty() -> Self {
2364        ResolvedIds {
2365            entries: BTreeMap::new(),
2366        }
2367    }
2368
2369    /// Returns if the set of IDs is empty.
2370    pub fn is_empty(&self) -> bool {
2371        self.entries.is_empty()
2372    }
2373
2374    /// Returns all of the [`GlobalId`]s in this set.
2375    pub fn collections(&self) -> impl Iterator<Item = &GlobalId> {
2376        self.entries.values().flat_map(|gids| gids.into_iter())
2377    }
2378
2379    /// Returns all of the [`CatalogItemId`]s in this set.
2380    pub fn items(&self) -> impl Iterator<Item = &CatalogItemId> {
2381        self.entries.keys()
2382    }
2383
2384    /// Returns if this set of IDs contains the provided [`CatalogItemId`].
2385    pub fn contains_item(&self, item: &CatalogItemId) -> bool {
2386        self.entries.contains_key(item)
2387    }
2388
2389    pub fn add_item(&mut self, item: CatalogItemId) {
2390        self.entries.insert(item, BTreeSet::new());
2391    }
2392
2393    pub fn remove_item(&mut self, item: &CatalogItemId) {
2394        self.entries.remove(item);
2395    }
2396
2397    /// Merges all entries from `other` into `self`.
2398    pub fn extend_from(&mut self, other: &ResolvedIds) {
2399        for (id, gids) in &other.entries {
2400            self.entries
2401                .entry(*id)
2402                .or_default()
2403                .extend(gids.iter().copied());
2404        }
2405    }
2406
2407    /// Create a new [`ResolvedIds`] that contains the elements from `self`
2408    /// where `predicate` returns `true`.
2409    pub fn retain_items<F>(&self, predicate: F) -> Self
2410    where
2411        F: Fn(&CatalogItemId) -> bool,
2412    {
2413        let mut new_ids = self.clone();
2414        new_ids
2415            .entries
2416            .retain(|item_id, _global_ids| predicate(item_id));
2417        new_ids
2418    }
2419}
2420
2421impl FromIterator<(CatalogItemId, GlobalId)> for ResolvedIds {
2422    fn from_iter<T: IntoIterator<Item = (CatalogItemId, GlobalId)>>(iter: T) -> Self {
2423        let mut ids = ResolvedIds::empty();
2424        ids.extend(iter);
2425        ids
2426    }
2427}
2428
2429impl Extend<(CatalogItemId, GlobalId)> for ResolvedIds {
2430    fn extend<T: IntoIterator<Item = (CatalogItemId, GlobalId)>>(&mut self, iter: T) {
2431        for (item_id, global_id) in iter {
2432            self.entries.entry(item_id).or_default().insert(global_id);
2433        }
2434    }
2435}
2436
2437/// A set of IDs references by the `HirRelationExpr` of an object.
2438#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2439pub struct DependencyIds(pub BTreeSet<CatalogItemId>);
2440
2441impl FromIterator<CatalogItemId> for DependencyIds {
2442    fn from_iter<T: IntoIterator<Item = CatalogItemId>>(iter: T) -> Self {
2443        DependencyIds(iter.into_iter().collect())
2444    }
2445}
2446
2447#[derive(Debug)]
2448pub struct DependencyVisitor<'a> {
2449    catalog: &'a dyn SessionCatalog,
2450    ids: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>,
2451}
2452
2453impl<'a> DependencyVisitor<'a> {
2454    pub fn new(catalog: &'a dyn SessionCatalog) -> Self {
2455        DependencyVisitor {
2456            catalog,
2457            ids: Default::default(),
2458        }
2459    }
2460}
2461
2462impl<'a, 'ast> Visit<'ast, Aug> for DependencyVisitor<'a> {
2463    fn visit_item_name(&mut self, item_name: &'ast <Aug as AstInfo>::ItemName) {
2464        if let ResolvedItemName::Item { id, version, .. } = item_name {
2465            let global_ids = self.ids.entry(*id).or_default();
2466            if let Some(item) = self.catalog.try_get_item(id) {
2467                global_ids.insert(item.at_version(*version).global_id());
2468            }
2469        }
2470    }
2471
2472    fn visit_data_type(&mut self, data_type: &'ast <Aug as AstInfo>::DataType) {
2473        match data_type {
2474            ResolvedDataType::AnonymousList(data_type) => self.visit_data_type(data_type),
2475            ResolvedDataType::AnonymousMap {
2476                key_type,
2477                value_type,
2478            } => {
2479                self.visit_data_type(key_type);
2480                self.visit_data_type(value_type);
2481            }
2482            ResolvedDataType::Named { id, .. } => {
2483                self.ids.entry(*id).or_default();
2484            }
2485            ResolvedDataType::Error => {}
2486        }
2487    }
2488}
2489
2490pub fn visit_dependencies<'ast, N>(catalog: &dyn SessionCatalog, node: &'ast N) -> ResolvedIds
2491where
2492    N: VisitNode<'ast, Aug> + 'ast,
2493{
2494    let mut visitor = DependencyVisitor::new(catalog);
2495    node.visit(&mut visitor);
2496    ResolvedIds::new(visitor.ids)
2497}
2498
2499#[derive(Debug)]
2500pub struct ItemDependencyModifier<'a> {
2501    pub modified: bool,
2502    pub id_map: &'a BTreeMap<CatalogItemId, CatalogItemId>,
2503}
2504
2505impl<'ast, 'a> VisitMut<'ast, Raw> for ItemDependencyModifier<'a> {
2506    fn visit_item_name_mut(&mut self, item_name: &mut RawItemName) {
2507        if let RawItemName::Id(id, _, _) = item_name {
2508            let parsed_id = id.parse::<CatalogItemId>().unwrap();
2509            if let Some(new_id) = self.id_map.get(&parsed_id) {
2510                *id = new_id.to_string();
2511                self.modified = true;
2512            }
2513        }
2514    }
2515}
2516
2517/// Updates any references in the provided AST node that are keys in `id_map`.
2518/// If an id is found it will be updated to the value of the key in `id_map`.
2519/// This assumes the names of the reference(s) are unmodified (e.g. each pair of
2520/// ids refer to an item of the same name, whose id has changed).
2521pub fn modify_dependency_item_ids<'ast, N>(
2522    node: &'ast mut N,
2523    id_map: &BTreeMap<CatalogItemId, CatalogItemId>,
2524) -> bool
2525where
2526    N: VisitMutNode<'ast, Raw>,
2527{
2528    let mut modifier = ItemDependencyModifier {
2529        id_map,
2530        modified: false,
2531    };
2532    node.visit_mut(&mut modifier);
2533
2534    modifier.modified
2535}
2536
2537// Used when displaying a view's source for human creation. If the name
2538// specified is the same as the name in the catalog, we don't use the ID format.
2539#[derive(Debug)]
2540pub struct NameSimplifier<'a> {
2541    pub catalog: &'a dyn SessionCatalog,
2542}
2543
2544impl<'ast, 'a> VisitMut<'ast, Aug> for NameSimplifier<'a> {
2545    fn visit_cluster_name_mut(&mut self, node: &mut ResolvedClusterName) {
2546        node.print_name = Some(self.catalog.get_cluster(node.id).name().into());
2547    }
2548
2549    fn visit_item_name_mut(&mut self, name: &mut ResolvedItemName) {
2550        if let ResolvedItemName::Item {
2551            id,
2552            full_name,
2553            print_id,
2554            ..
2555        } = name
2556        {
2557            let item = self.catalog.get_item(id);
2558            let catalog_full_name = self.catalog.resolve_full_name(item.name());
2559            if catalog_full_name == *full_name {
2560                *print_id = false;
2561            }
2562        }
2563    }
2564
2565    fn visit_data_type_mut(&mut self, name: &mut ResolvedDataType) {
2566        if let ResolvedDataType::Named {
2567            id,
2568            full_name,
2569            print_id,
2570            ..
2571        } = name
2572        {
2573            let item = self.catalog.get_item(id);
2574            let catalog_full_name = self.catalog.resolve_full_name(item.name());
2575            if catalog_full_name == *full_name {
2576                *print_id = false;
2577            }
2578        }
2579    }
2580}
2581
2582/// Returns the [`CatalogItemId`] dependencies the provided `node` has.
2583///
2584/// _DOES NOT_ resolve names, simply does a recursive walk through an object to
2585/// find all of the IDs.
2586pub fn dependencies<'ast, N>(node: &'ast N) -> Result<BTreeSet<CatalogItemId>, anyhow::Error>
2587where
2588    N: VisitNode<'ast, Raw>,
2589{
2590    let mut visitor = IdDependencVisitor::default();
2591    node.visit(&mut visitor);
2592    match visitor.error {
2593        Some(error) => Err(error),
2594        None => Ok(visitor.ids),
2595    }
2596}
2597
2598#[derive(Debug, Default)]
2599struct IdDependencVisitor {
2600    ids: BTreeSet<CatalogItemId>,
2601    error: Option<anyhow::Error>,
2602}
2603
2604impl<'ast> Visit<'ast, Raw> for IdDependencVisitor {
2605    fn visit_item_name(&mut self, node: &'ast <Raw as AstInfo>::ItemName) {
2606        // Bail early if we're already in an error state.
2607        if self.error.is_some() {
2608            return;
2609        }
2610
2611        match node {
2612            // Nothing to do! We don't lookup names.
2613            RawItemName::Name(_) => (),
2614            RawItemName::Id(id, _name, _version) => match id.parse::<CatalogItemId>() {
2615                Ok(id) => {
2616                    self.ids.insert(id);
2617                }
2618                Err(e) => {
2619                    self.error = Some(e);
2620                }
2621            },
2622        }
2623    }
2624}
2625
2626#[cfg(test)]
2627mod tests {
2628    use proptest::prelude::*;
2629
2630    use super::*;
2631
2632    #[mz_ore::test]
2633    fn proptest_schema_id_roundtrips() {
2634        fn testcase(og: SchemaId) {
2635            let s = og.to_string();
2636            let rnd: SchemaId = s.parse().unwrap();
2637            assert_eq!(og, rnd);
2638        }
2639
2640        proptest!(|(id in any::<SchemaId>())| {
2641            testcase(id);
2642        })
2643    }
2644
2645    #[mz_ore::test]
2646    fn proptest_database_id_roundtrips() {
2647        fn testcase(og: DatabaseId) {
2648            let s = og.to_string();
2649            let rnd: DatabaseId = s.parse().unwrap();
2650            assert_eq!(og, rnd);
2651        }
2652
2653        proptest!(|(id in any::<DatabaseId>())| {
2654            testcase(id);
2655        })
2656    }
2657
2658    #[mz_ore::test]
2659    fn test_schema_id_from_str() {
2660        assert_eq!("s5".parse::<SchemaId>().unwrap(), SchemaId::System(5));
2661        assert_eq!("u5".parse::<SchemaId>().unwrap(), SchemaId::User(5));
2662
2663        // Regression test for a panic on multi-byte leading characters, where
2664        // slicing off a single byte landed inside a UTF-8 char boundary (SQL-195).
2665        for invalid in ["ü1", "ü", "é42", "🦀7", "", "x1", "s"] {
2666            assert!(
2667                invalid.parse::<SchemaId>().is_err(),
2668                "expected {invalid:?} to fail to parse"
2669            );
2670        }
2671    }
2672
2673    #[mz_ore::test]
2674    fn test_database_id_from_str() {
2675        assert_eq!("s5".parse::<DatabaseId>().unwrap(), DatabaseId::System(5));
2676        assert_eq!("u5".parse::<DatabaseId>().unwrap(), DatabaseId::User(5));
2677
2678        // Regression test for a panic on multi-byte leading characters, where
2679        // slicing off a single byte landed inside a UTF-8 char boundary (SQL-195).
2680        for invalid in ["ü1", "ü", "é42", "🦀7", "", "x1", "u"] {
2681            assert!(
2682                invalid.parse::<DatabaseId>().is_err(),
2683                "expected {invalid:?} to fail to parse"
2684            );
2685        }
2686    }
2687}