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, CatalogType, 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::Index(item_id)
1236            | CommentObjectId::Func(item_id)
1237            | CommentObjectId::Connection(item_id)
1238            | CommentObjectId::Type(item_id)
1239            | CommentObjectId::Secret(item_id) => ObjectId::Item(item_id),
1240            CommentObjectId::Role(id) => ObjectId::Role(id),
1241            CommentObjectId::Database(id) => ObjectId::Database(id),
1242            CommentObjectId::Schema(id) => ObjectId::Schema(id),
1243            CommentObjectId::Cluster(id) => ObjectId::Cluster(id),
1244            CommentObjectId::ClusterReplica(id) => ObjectId::ClusterReplica(id),
1245            CommentObjectId::NetworkPolicy(id) => ObjectId::NetworkPolicy(id),
1246        }
1247    }
1248}
1249
1250#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1251pub enum SystemObjectId {
1252    /// The ID of a specific object.
1253    Object(ObjectId),
1254    /// Identifier for the entire system.
1255    System,
1256}
1257
1258impl SystemObjectId {
1259    pub fn object_id(&self) -> Option<&ObjectId> {
1260        match self {
1261            SystemObjectId::Object(object_id) => Some(object_id),
1262            SystemObjectId::System => None,
1263        }
1264    }
1265
1266    pub fn is_system(&self) -> bool {
1267        matches!(self, SystemObjectId::System)
1268    }
1269}
1270
1271impl From<ObjectId> for SystemObjectId {
1272    fn from(id: ObjectId) -> Self {
1273        SystemObjectId::Object(id)
1274    }
1275}
1276
1277/// Comments can be applied to multiple kinds of objects (e.g. Tables and Role), so we need a way
1278/// to represent these different types and their IDs (e.g. [`CatalogItemId`] and [`RoleId`]), as
1279/// well as the inner kind of object that is represented, e.g. [`CatalogItemId`] is used to
1280/// identify both Tables and Views. No other kind of ID encapsulates all of this, hence this new
1281/// "*Id" type.
1282///
1283/// New variants here also need to be added to the durable counterpart
1284/// `mz_catalog_protos::objects::CommentObject` and to both CASE expressions in
1285/// the `mz_internal.mz_comments` materialized view (`MZ_COMMENTS` in
1286/// `mz-catalog::builtin::mz_internal`). The MV reads `CommentObject` out of
1287/// `mz_catalog_raw` as serde JSON.
1288#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
1289pub enum CommentObjectId {
1290    Table(CatalogItemId),
1291    View(CatalogItemId),
1292    MaterializedView(CatalogItemId),
1293    Source(CatalogItemId),
1294    Sink(CatalogItemId),
1295    Index(CatalogItemId),
1296    Func(CatalogItemId),
1297    Connection(CatalogItemId),
1298    Type(CatalogItemId),
1299    Secret(CatalogItemId),
1300    Role(RoleId),
1301    Database(DatabaseId),
1302    Schema((ResolvedDatabaseSpecifier, SchemaSpecifier)),
1303    Cluster(ClusterId),
1304    ClusterReplica((ClusterId, ReplicaId)),
1305    NetworkPolicy(NetworkPolicyId),
1306}
1307
1308/// Whether to resolve an name in the types namespace, the functions namespace,
1309/// or the relations namespace. It is possible to resolve name in multiple
1310/// namespaces, in which case types are preferred to functions are preferred to
1311/// relations.
1312// NOTE(benesch,sploiselle): The fact that some names are looked up in multiple
1313// namespaces is a bit dubious, and stems from the fact that we don't
1314// automatically create types for relations (see database-issues#7142). It's possible that we
1315// don't allow names to be looked up in multiple namespaces (i.e., this becomes
1316// `enum ItemResolutionNamespace`), but it's also possible that the design of
1317// the `DOC ON TYPE` option means we're forever stuck with this complexity.
1318#[derive(Debug, Clone, Copy)]
1319struct ItemResolutionConfig {
1320    types: bool,
1321    functions: bool,
1322    relations: bool,
1323}
1324
1325#[derive(Debug)]
1326pub struct NameResolver<'a> {
1327    catalog: &'a dyn SessionCatalog,
1328    ctes: BTreeMap<String, LocalId>,
1329    /// The next `LocalId` to allocate for a CTE. Never decremented, so every
1330    /// CTE in the statement gets a unique id, even when CTE names shadow each
1331    /// other. Later phases (e.g., HIR lowering's `CteMap`) key CTEs by
1332    /// `LocalId` and rely on this uniqueness.
1333    next_cte_id: u64,
1334    status: Result<(), PlanError>,
1335    ids: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>,
1336}
1337
1338impl<'a> NameResolver<'a> {
1339    fn new(catalog: &'a dyn SessionCatalog) -> NameResolver<'a> {
1340        NameResolver {
1341            catalog,
1342            ctes: BTreeMap::new(),
1343            next_cte_id: 0,
1344            status: Ok(()),
1345            ids: BTreeMap::new(),
1346        }
1347    }
1348
1349    fn allocate_cte_id(&mut self) -> LocalId {
1350        let id = LocalId::new(self.next_cte_id);
1351        self.next_cte_id += 1;
1352        id
1353    }
1354
1355    fn resolve_data_type(&mut self, data_type: RawDataType) -> Result<ResolvedDataType, PlanError> {
1356        match data_type {
1357            RawDataType::Array(elem_type) => {
1358                let name = elem_type.to_string();
1359                match self.resolve_data_type(*elem_type)? {
1360                    ResolvedDataType::AnonymousList(_) | ResolvedDataType::AnonymousMap { .. } => {
1361                        sql_bail!("type \"{}[]\" does not exist", name)
1362                    }
1363                    ResolvedDataType::Named { id, modifiers, .. } => {
1364                        let element_item = self.catalog.get_item(&id);
1365                        let array_item = match element_item.type_details() {
1366                            Some(CatalogTypeDetails {
1367                                array_id: Some(array_id),
1368                                ..
1369                            }) => self.catalog.get_item(array_id),
1370                            Some(_) => sql_bail!("type \"{}[]\" does not exist", name),
1371                            None => {
1372                                // Resolution should never produce a
1373                                // `ResolvedDataType::Named` with an ID of a
1374                                // non-type, but we error gracefully just in
1375                                // case.
1376                                sql_bail!(
1377                                    "internal error: {} does not refer to a type",
1378                                    self.catalog
1379                                        .resolve_full_name(element_item.name())
1380                                        .to_string()
1381                                        .quoted()
1382                                );
1383                            }
1384                        };
1385                        self.ids.insert(array_item.id(), BTreeSet::new());
1386                        Ok(ResolvedDataType::Named {
1387                            id: array_item.id(),
1388                            qualifiers: array_item.name().qualifiers.clone(),
1389                            full_name: self.catalog.resolve_full_name(array_item.name()),
1390                            modifiers,
1391                            print_id: true,
1392                        })
1393                    }
1394                    ResolvedDataType::Error => sql_bail!("type \"{}[]\" does not exist", name),
1395                }
1396            }
1397            RawDataType::List(elem_type) => {
1398                let elem_type = self.resolve_data_type(*elem_type)?;
1399                Ok(ResolvedDataType::AnonymousList(Box::new(elem_type)))
1400            }
1401            RawDataType::Map {
1402                key_type,
1403                value_type,
1404            } => {
1405                let key_type = self.resolve_data_type(*key_type)?;
1406                let value_type = self.resolve_data_type(*value_type)?;
1407                Ok(ResolvedDataType::AnonymousMap {
1408                    key_type: Box::new(key_type),
1409                    value_type: Box::new(value_type),
1410                })
1411            }
1412            RawDataType::Other { name, typ_mod } => {
1413                let (full_name, item) = match name {
1414                    RawItemName::Name(name) => {
1415                        let name = normalize::unresolved_item_name(name)?;
1416                        let item = self.catalog.resolve_type(&name)?;
1417                        let full_name = self.catalog.resolve_full_name(item.name());
1418                        (full_name, item)
1419                    }
1420                    RawItemName::Id(id, name, version) => {
1421                        let id: CatalogItemId = id.parse()?;
1422                        let item = match self.catalog.try_get_item(&id) {
1423                            Some(item) => item,
1424                            None => return Err(PlanError::InvalidId(id)),
1425                        };
1426                        let full_name = normalize::full_name(name)?;
1427                        if version.is_some() {
1428                            sql_bail!("specifying a version for a type reference is not supported");
1429                        }
1430
1431                        (full_name, item)
1432                    }
1433                };
1434                self.ids.insert(item.id(), BTreeSet::new());
1435                // If this is a named array type, then make sure to include the element reference
1436                // in the resolved IDs. This helps ensure that named array types are resolved the
1437                // same as an array type with the same element type. For example, `int4[]` and
1438                // `_int4` should have the same set of resolved IDs.
1439                if let Some(CatalogTypeDetails {
1440                    typ: CatalogType::Array { element_reference },
1441                    ..
1442                }) = item.type_details()
1443                {
1444                    self.ids.insert(*element_reference, BTreeSet::new());
1445                }
1446                Ok(ResolvedDataType::Named {
1447                    id: item.id(),
1448                    qualifiers: item.name().qualifiers.clone(),
1449                    full_name,
1450                    modifiers: typ_mod,
1451                    print_id: true,
1452                })
1453            }
1454        }
1455    }
1456
1457    fn resolve_item_name(
1458        &mut self,
1459        item_name: RawItemName,
1460        config: ItemResolutionConfig,
1461    ) -> ResolvedItemName {
1462        match item_name {
1463            RawItemName::Name(name) => self.resolve_item_name_name(name, config),
1464            RawItemName::Id(id, raw_name, version) => {
1465                self.resolve_item_name_id(id, raw_name, version)
1466            }
1467        }
1468    }
1469
1470    fn resolve_item_name_name(
1471        &mut self,
1472        raw_name: UnresolvedItemName,
1473        config: ItemResolutionConfig,
1474    ) -> ResolvedItemName {
1475        let raw_name = match normalize::unresolved_item_name(raw_name) {
1476            Ok(raw_name) => raw_name,
1477            Err(e) => {
1478                if self.status.is_ok() {
1479                    self.status = Err(e);
1480                }
1481                return ResolvedItemName::Error;
1482            }
1483        };
1484
1485        let mut r: Result<&dyn CatalogItem, CatalogError> =
1486            Err(CatalogError::UnknownItem(raw_name.to_string()));
1487
1488        if r.is_err() && config.types {
1489            r = self.catalog.resolve_type(&raw_name);
1490        }
1491
1492        if r.is_err() && config.functions {
1493            r = self.catalog.resolve_function(&raw_name);
1494        }
1495
1496        if r.is_err() && config.relations {
1497            // Check if unqualified name refers to a CTE.
1498            //
1499            // Note that this is done in non-function contexts as CTEs
1500            // are treated as relations.
1501            if raw_name.database.is_none() && raw_name.schema.is_none() {
1502                let norm_name = normalize::ident(Ident::new_unchecked(&raw_name.item));
1503                if let Some(id) = self.ctes.get(&norm_name) {
1504                    return ResolvedItemName::Cte {
1505                        id: *id,
1506                        name: norm_name,
1507                    };
1508                }
1509            }
1510            r = self.catalog.resolve_item(&raw_name);
1511        };
1512
1513        match r {
1514            Ok(item) => {
1515                // Record the item at its current version.
1516                let item = item.at_version(RelationVersionSelector::Latest);
1517                self.ids
1518                    .entry(item.id())
1519                    .or_default()
1520                    .insert(item.global_id());
1521                let print_id = !matches!(
1522                    item.item_type(),
1523                    CatalogItemType::Func | CatalogItemType::Type
1524                );
1525                let alter_table_enabled =
1526                    self.catalog.system_vars().enable_alter_table_add_column();
1527                let version = match item.latest_version() {
1528                    // Only track the version of referenced object if the feature is enabled.
1529                    Some(v) if item.id().is_user() && alter_table_enabled => {
1530                        RelationVersionSelector::Specific(v)
1531                    }
1532                    _ => RelationVersionSelector::Latest,
1533                };
1534
1535                ResolvedItemName::Item {
1536                    id: item.id(),
1537                    qualifiers: item.name().qualifiers.clone(),
1538                    full_name: self.catalog.resolve_full_name(item.name()),
1539                    print_id,
1540                    version,
1541                }
1542            }
1543            Err(mut e) => {
1544                if self.status.is_ok() {
1545                    match &mut e {
1546                        CatalogError::UnknownFunction {
1547                            name: _,
1548                            alternative,
1549                        } => {
1550                            // Suggest using the `jsonb_` version of `json_`
1551                            // functions that do not exist.
1552                            if raw_name.database.is_none()
1553                                && (raw_name.schema.is_none()
1554                                    || raw_name.schema.as_deref() == Some("pg_catalog")
1555                                        && raw_name.item.starts_with("json_"))
1556                            {
1557                                let jsonb_name = PartialItemName {
1558                                    item: raw_name.item.replace("json_", "jsonb_"),
1559                                    ..raw_name
1560                                };
1561                                if self.catalog.resolve_function(&jsonb_name).is_ok() {
1562                                    *alternative = Some(jsonb_name.to_string());
1563                                }
1564                            }
1565                        }
1566                        _ => (),
1567                    }
1568
1569                    self.status = Err(e.into());
1570                }
1571                ResolvedItemName::Error
1572            }
1573        }
1574    }
1575
1576    fn resolve_item_name_id(
1577        &mut self,
1578        id: String,
1579        raw_name: UnresolvedItemName,
1580        version: Option<Version>,
1581    ) -> ResolvedItemName {
1582        let id: CatalogItemId = match id.parse() {
1583            Ok(id) => id,
1584            Err(e) => {
1585                if self.status.is_ok() {
1586                    self.status = Err(e.into());
1587                }
1588                return ResolvedItemName::Error;
1589            }
1590        };
1591        let item = match self.catalog.try_get_item(&id) {
1592            Some(item) => item,
1593            None => {
1594                if self.status.is_ok() {
1595                    self.status = Err(PlanError::InvalidId(id));
1596                }
1597                return ResolvedItemName::Error;
1598            }
1599        };
1600        let alter_table_enabled = self.catalog.system_vars().enable_alter_table_add_column();
1601        let version = match version {
1602            // If there isn't a version specified, and this item supports versioning, track the
1603            // latest.
1604            None => match item.latest_version() {
1605                // Only track the version of the referenced object, if the feature is enabled.
1606                Some(v) if alter_table_enabled => RelationVersionSelector::Specific(v),
1607                _ => RelationVersionSelector::Latest,
1608            },
1609            // Note: Return the specific version if one is specified, even if the feature is off.
1610            Some(v) => {
1611                let specified_version = RelationVersion::from(v);
1612                match item.latest_version() {
1613                    Some(latest) if latest >= specified_version => {
1614                        RelationVersionSelector::Specific(specified_version)
1615                    }
1616                    _ => {
1617                        if self.status.is_ok() {
1618                            self.status = Err(PlanError::InvalidVersion {
1619                                name: item.name().item.clone(),
1620                                version: v.to_string(),
1621                            })
1622                        }
1623                        return ResolvedItemName::Error;
1624                    }
1625                }
1626            }
1627        };
1628        let item = item.at_version(version);
1629        self.ids
1630            .entry(item.id())
1631            .or_default()
1632            .insert(item.global_id());
1633
1634        let full_name = match normalize::full_name(raw_name) {
1635            Ok(full_name) => full_name,
1636            Err(e) => {
1637                if self.status.is_ok() {
1638                    self.status = Err(e);
1639                }
1640                return ResolvedItemName::Error;
1641            }
1642        };
1643        ResolvedItemName::Item {
1644            id,
1645            qualifiers: item.name().qualifiers.clone(),
1646            full_name,
1647            print_id: true,
1648            version,
1649        }
1650    }
1651}
1652
1653impl<'a> Fold<Raw, Aug> for NameResolver<'a> {
1654    fn fold_nested_statement(
1655        &mut self,
1656        stmt: <Raw as AstInfo>::NestedStatement,
1657    ) -> <Aug as AstInfo>::NestedStatement {
1658        stmt
1659    }
1660
1661    fn fold_query(&mut self, q: Query<Raw>) -> Query<Aug> {
1662        // Retain the old values of various CTE names so that we can restore them after we're done
1663        // planning this SELECT.
1664        let mut shadowed_cte_ids = Vec::new();
1665
1666        // A reused identifier indicates a reused name.
1667        use itertools::Itertools;
1668        if let Some(ident) = q.ctes.bound_identifiers().duplicates().next() {
1669            self.status = Err(sql_err!(
1670                "WITH query name \"{}\" specified more than once",
1671                normalize::ident_ref(ident),
1672            ));
1673        }
1674
1675        let ctes: CteBlock<Aug> = match q.ctes {
1676            CteBlock::Simple(ctes) => {
1677                let mut result_ctes = Vec::<Cte<Aug>>::new();
1678
1679                for cte in ctes.into_iter() {
1680                    let cte_name = normalize::ident(cte.alias.name.clone());
1681                    let local_id = self.allocate_cte_id();
1682
1683                    result_ctes.push(Cte {
1684                        alias: cte.alias,
1685                        id: local_id,
1686                        query: self.fold_query(cte.query),
1687                    });
1688
1689                    let shadowed_id = self.ctes.insert(cte_name.clone(), local_id);
1690                    shadowed_cte_ids.push((cte_name, shadowed_id));
1691                }
1692                CteBlock::Simple(result_ctes)
1693            }
1694            CteBlock::MutuallyRecursive(MutRecBlock { options, ctes }) => {
1695                let mut result_ctes = Vec::<CteMutRec<Aug>>::new();
1696
1697                // All bindings go into scope before any definition is walked,
1698                // so that the definitions can refer to each other.
1699                let mut local_ids = Vec::with_capacity(ctes.len());
1700                for cte in ctes.iter() {
1701                    let cte_name = normalize::ident(cte.name.clone());
1702                    let local_id = self.allocate_cte_id();
1703                    let shadowed_id = self.ctes.insert(cte_name.clone(), local_id);
1704                    shadowed_cte_ids.push((cte_name, shadowed_id));
1705                    local_ids.push(local_id);
1706                }
1707
1708                for (cte, local_id) in ctes.into_iter().zip_eq(local_ids) {
1709                    let columns = cte
1710                        .columns
1711                        .into_iter()
1712                        .map(|column| self.fold_cte_mut_rec_column_def(column))
1713                        .collect();
1714                    let query = self.fold_query(cte.query);
1715                    result_ctes.push(CteMutRec {
1716                        name: cte.name,
1717                        columns,
1718                        id: local_id,
1719                        query,
1720                    });
1721                }
1722                CteBlock::MutuallyRecursive(MutRecBlock {
1723                    options: options
1724                        .into_iter()
1725                        .map(|option| self.fold_mut_rec_block_option(option))
1726                        .collect(),
1727                    ctes: result_ctes,
1728                })
1729            }
1730        };
1731
1732        let result = Query {
1733            ctes,
1734            // Queries can be recursive, so need the ability to grow the stack.
1735            body: mz_ore::stack::maybe_grow(|| self.fold_set_expr(q.body)),
1736            limit: q.limit.map(|l| self.fold_limit(l)),
1737            offset: q.offset.map(|l| self.fold_expr(l)),
1738            order_by: q
1739                .order_by
1740                .into_iter()
1741                .map(|c| self.fold_order_by_expr(c))
1742                .collect(),
1743        };
1744
1745        // Restore the old values of the CTEs.
1746        for (name, value) in shadowed_cte_ids.iter() {
1747            match value {
1748                Some(value) => {
1749                    self.ctes.insert(name.to_string(), value.clone());
1750                }
1751                None => {
1752                    self.ctes.remove(name);
1753                }
1754            };
1755        }
1756
1757        result
1758    }
1759
1760    fn fold_cte_id(&mut self, _id: <Raw as AstInfo>::CteId) -> <Aug as AstInfo>::CteId {
1761        panic!("this should have been handled when walking the CTE");
1762    }
1763
1764    fn fold_item_name(
1765        &mut self,
1766        item_name: <Raw as AstInfo>::ItemName,
1767    ) -> <Aug as AstInfo>::ItemName {
1768        self.resolve_item_name(
1769            item_name,
1770            // By default, when resolving an item name, we assume only relations
1771            // should be in scope.
1772            ItemResolutionConfig {
1773                functions: false,
1774                types: false,
1775                relations: true,
1776            },
1777        )
1778    }
1779
1780    fn fold_column_name(&mut self, column_name: ast::ColumnName<Raw>) -> ast::ColumnName<Aug> {
1781        let item_name = self.resolve_item_name(
1782            column_name.relation,
1783            ItemResolutionConfig {
1784                functions: false,
1785                types: true,
1786                relations: true,
1787            },
1788        );
1789
1790        match &item_name {
1791            ResolvedItemName::Item {
1792                id,
1793                full_name,
1794                version,
1795                qualifiers: _,
1796                print_id: _,
1797            } => {
1798                let item = self.catalog.get_item(id).at_version(*version);
1799                let name = normalize::column_name(column_name.column.clone());
1800
1801                let maybe_desc = match item.type_details() {
1802                    Some(details) => match details.typ.desc(self.catalog) {
1803                        Ok(desc) => desc.map(Cow::Owned),
1804                        Err(e) => {
1805                            if self.status.is_ok() {
1806                                self.status = Err(e);
1807                            }
1808                            return ast::ColumnName {
1809                                relation: ResolvedItemName::Error,
1810                                column: ResolvedColumnReference::Error,
1811                            };
1812                        }
1813                    },
1814                    None => item.relation_desc(),
1815                };
1816                let Some(desc) = maybe_desc else {
1817                    if self.status.is_ok() {
1818                        self.status = Err(PlanError::ItemWithoutColumns {
1819                            name: full_name.to_string(),
1820                            item_type: item.item_type(),
1821                        });
1822                    }
1823                    return ast::ColumnName {
1824                        relation: ResolvedItemName::Error,
1825                        column: ResolvedColumnReference::Error,
1826                    };
1827                };
1828
1829                let Some((index, _typ)) = desc.get_by_name(&name) else {
1830                    if self.status.is_ok() {
1831                        let similar = desc.iter_similar_names(&name).cloned().collect();
1832                        self.status = Err(PlanError::UnknownColumn {
1833                            table: Some(full_name.clone().into()),
1834                            column: name,
1835                            similar,
1836                        })
1837                    }
1838                    return ast::ColumnName {
1839                        relation: ResolvedItemName::Error,
1840                        column: ResolvedColumnReference::Error,
1841                    };
1842                };
1843
1844                ast::ColumnName {
1845                    relation: item_name,
1846                    column: ResolvedColumnReference::Column { name, index },
1847                }
1848            }
1849            ResolvedItemName::Cte { .. } | ResolvedItemName::Error => ast::ColumnName {
1850                relation: ResolvedItemName::Error,
1851                column: ResolvedColumnReference::Error,
1852            },
1853        }
1854    }
1855
1856    fn fold_column_reference(
1857        &mut self,
1858        _node: <Raw as AstInfo>::ColumnReference,
1859    ) -> <Aug as AstInfo>::ColumnReference {
1860        // Do not call this function directly; instead resolve through `fold_column_name`
1861        ResolvedColumnReference::Error
1862    }
1863
1864    fn fold_data_type(
1865        &mut self,
1866        data_type: <Raw as AstInfo>::DataType,
1867    ) -> <Aug as AstInfo>::DataType {
1868        match self.resolve_data_type(data_type) {
1869            Ok(data_type) => data_type,
1870            Err(e) => {
1871                if self.status.is_ok() {
1872                    self.status = Err(e);
1873                }
1874                ResolvedDataType::Error
1875            }
1876        }
1877    }
1878
1879    fn fold_schema_name(
1880        &mut self,
1881        name: <Raw as AstInfo>::SchemaName,
1882    ) -> <Aug as AstInfo>::SchemaName {
1883        let norm_name = match normalize::unresolved_schema_name(name) {
1884            Ok(norm_name) => norm_name,
1885            Err(e) => {
1886                if self.status.is_ok() {
1887                    self.status = Err(e);
1888                }
1889                return ResolvedSchemaName::Error;
1890            }
1891        };
1892
1893        // Special case for mz_temp: with lazy temporary schema creation, the temp
1894        // schema may not exist yet. Return a resolved name with SchemaSpecifier::Temporary
1895        // so that downstream code can handle it appropriately (e.g., return a proper error).
1896        if norm_name.database.is_none() && norm_name.schema == mz_repr::namespaces::MZ_TEMP_SCHEMA {
1897            return ResolvedSchemaName::Schema {
1898                database_spec: ResolvedDatabaseSpecifier::Ambient,
1899                schema_spec: SchemaSpecifier::Temporary,
1900                full_name: FullSchemaName {
1901                    database: RawDatabaseSpecifier::Ambient,
1902                    schema: mz_repr::namespaces::MZ_TEMP_SCHEMA.to_string(),
1903                },
1904            };
1905        }
1906
1907        match self
1908            .catalog
1909            .resolve_schema(norm_name.database.as_deref(), norm_name.schema.as_str())
1910        {
1911            Ok(schema) => {
1912                let raw_database_spec = match schema.database() {
1913                    ResolvedDatabaseSpecifier::Ambient => RawDatabaseSpecifier::Ambient,
1914                    ResolvedDatabaseSpecifier::Id(id) => {
1915                        RawDatabaseSpecifier::Name(self.catalog.get_database(id).name().to_string())
1916                    }
1917                };
1918                ResolvedSchemaName::Schema {
1919                    database_spec: schema.database().clone(),
1920                    schema_spec: schema.id().clone(),
1921                    full_name: FullSchemaName {
1922                        database: raw_database_spec,
1923                        schema: schema.name().schema.clone(),
1924                    },
1925                }
1926            }
1927            Err(e) => {
1928                if self.status.is_ok() {
1929                    self.status = Err(e.into());
1930                }
1931                ResolvedSchemaName::Error
1932            }
1933        }
1934    }
1935
1936    fn fold_database_name(
1937        &mut self,
1938        database_name: <Raw as AstInfo>::DatabaseName,
1939    ) -> <Aug as AstInfo>::DatabaseName {
1940        match self.catalog.resolve_database(database_name.0.as_str()) {
1941            Ok(database) => ResolvedDatabaseName::Database {
1942                id: database.id(),
1943                name: database_name.0.into_string(),
1944            },
1945            Err(e) => {
1946                if self.status.is_ok() {
1947                    self.status = Err(e.into());
1948                }
1949                ResolvedDatabaseName::Error
1950            }
1951        }
1952    }
1953
1954    fn fold_cluster_name(
1955        &mut self,
1956        cluster_name: <Raw as AstInfo>::ClusterName,
1957    ) -> <Aug as AstInfo>::ClusterName {
1958        match cluster_name {
1959            RawClusterName::Unresolved(ident) => {
1960                match self.catalog.resolve_cluster(Some(ident.as_str())) {
1961                    Ok(cluster) => ResolvedClusterName {
1962                        id: cluster.id(),
1963                        print_name: None,
1964                    },
1965                    Err(e) => {
1966                        self.status = Err(e.into());
1967                        ResolvedClusterName {
1968                            // The ID is arbitrary here; we just need some dummy
1969                            // value to return.
1970                            id: ClusterId::system(0).expect("0 is a valid ID"),
1971                            print_name: None,
1972                        }
1973                    }
1974                }
1975            }
1976            RawClusterName::Resolved(ident) => match ident.parse() {
1977                Ok(id) => ResolvedClusterName {
1978                    id,
1979                    print_name: None,
1980                },
1981                Err(e) => {
1982                    self.status = Err(e.into());
1983                    ResolvedClusterName {
1984                        // The ID is arbitrary here; we just need some dummy
1985                        // value to return.
1986                        id: ClusterId::system(0).expect("0 is a valid ID"),
1987                        print_name: None,
1988                    }
1989                }
1990            },
1991        }
1992    }
1993
1994    fn fold_with_option_value(
1995        &mut self,
1996        node: mz_sql_parser::ast::WithOptionValue<Raw>,
1997    ) -> mz_sql_parser::ast::WithOptionValue<Aug> {
1998        use mz_sql_parser::ast::WithOptionValue::*;
1999        match node {
2000            Sequence(vs) => Sequence(
2001                vs.into_iter()
2002                    .map(|v| self.fold_with_option_value(v))
2003                    .collect(),
2004            ),
2005            Map(map) => Map(map
2006                .into_iter()
2007                .map(|(k, v)| (k, self.fold_with_option_value(v)))
2008                .collect()),
2009            Value(v) => Value(self.fold_value(v)),
2010            DataType(dt) => DataType(self.fold_data_type(dt)),
2011            Secret(secret) => {
2012                let item_name = self.fold_item_name(secret);
2013                match &item_name {
2014                    ResolvedItemName::Item { id, .. } => {
2015                        let item = self.catalog.get_item(id);
2016                        if item.item_type() != CatalogItemType::Secret {
2017                            self.status =
2018                                Err(PlanError::InvalidSecret(Box::new(item_name.clone())));
2019                        }
2020                    }
2021                    ResolvedItemName::Cte { .. } => {
2022                        self.status = Err(PlanError::InvalidSecret(Box::new(item_name.clone())));
2023                    }
2024                    ResolvedItemName::Error => {}
2025                }
2026                Secret(item_name)
2027            }
2028            Item(obj) => {
2029                let item_name = self.fold_item_name(obj);
2030                match &item_name {
2031                    ResolvedItemName::Item { .. } => {}
2032                    ResolvedItemName::Cte { .. } => {
2033                        self.status = Err(PlanError::InvalidObject(Box::new(item_name.clone())));
2034                    }
2035                    ResolvedItemName::Error => {}
2036                }
2037                Item(item_name)
2038            }
2039            UnresolvedItemName(name) => UnresolvedItemName(self.fold_unresolved_item_name(name)),
2040            Ident(name) => Ident(self.fold_ident(name)),
2041            Expr(e) => Expr(self.fold_expr(e)),
2042            ClusterReplicas(replicas) => ClusterReplicas(
2043                replicas
2044                    .into_iter()
2045                    .map(|r| self.fold_replica_definition(r))
2046                    .collect(),
2047            ),
2048            ConnectionKafkaBroker(broker) => ConnectionKafkaBroker(self.fold_kafka_broker(broker)),
2049            ConnectionAwsPrivatelink(privatelink) => {
2050                ConnectionAwsPrivatelink(self.fold_connection_default_aws_privatelink(privatelink))
2051            }
2052            KafkaMatchingBrokerRule(x) => {
2053                KafkaMatchingBrokerRule(self.fold_kafka_matching_broker_rule(x))
2054            }
2055            RetainHistoryFor(value) => RetainHistoryFor(self.fold_value(value)),
2056            Refresh(refresh) => Refresh(self.fold_refresh_option_value(refresh)),
2057            ClusterScheduleOptionValue(value) => ClusterScheduleOptionValue(value),
2058            ClusterAlterStrategy(value) => {
2059                ClusterAlterStrategy(self.fold_cluster_alter_option_value(value))
2060            }
2061            NetworkPolicyRules(rules) => NetworkPolicyRules(
2062                rules
2063                    .into_iter()
2064                    .map(|r| self.fold_network_policy_rule_definition(r))
2065                    .collect(),
2066            ),
2067        }
2068    }
2069
2070    fn fold_role_name(&mut self, name: <Raw as AstInfo>::RoleName) -> <Aug as AstInfo>::RoleName {
2071        match self.catalog.resolve_role(name.as_str()) {
2072            Ok(role) => ResolvedRoleName {
2073                id: role.id(),
2074                name: role.name().to_string(),
2075            },
2076            Err(e) => {
2077                if self.status.is_ok() {
2078                    self.status = Err(e.into());
2079                }
2080                // garbage value that will be ignored since there's an error.
2081                ResolvedRoleName {
2082                    id: RoleId::User(0),
2083                    name: "".to_string(),
2084                }
2085            }
2086        }
2087    }
2088
2089    fn fold_network_policy_name(
2090        &mut self,
2091        name: <Raw as AstInfo>::NetworkPolicyName,
2092    ) -> <Aug as AstInfo>::NetworkPolicyName {
2093        match self.catalog.resolve_network_policy(&name.to_string()) {
2094            Ok(policy) => ResolvedNetworkPolicyName {
2095                id: policy.id(),
2096                name: policy.name().to_string(),
2097            },
2098            Err(e) => {
2099                if self.status.is_ok() {
2100                    self.status = Err(e.into());
2101                }
2102                // garbage value that will be ignored since there's an error.
2103                ResolvedNetworkPolicyName {
2104                    id: NetworkPolicyId::User(0),
2105                    name: "".to_string(),
2106                }
2107            }
2108        }
2109    }
2110
2111    fn fold_object_name(
2112        &mut self,
2113        name: <Raw as AstInfo>::ObjectName,
2114    ) -> <Aug as AstInfo>::ObjectName {
2115        match name {
2116            UnresolvedObjectName::Cluster(name) => ResolvedObjectName::Cluster(
2117                self.fold_cluster_name(RawClusterName::Unresolved(name)),
2118            ),
2119            UnresolvedObjectName::ClusterReplica(name) => {
2120                match self.catalog.resolve_cluster_replica(&name) {
2121                    Ok(cluster_replica) => {
2122                        ResolvedObjectName::ClusterReplica(ResolvedClusterReplicaName {
2123                            cluster_id: cluster_replica.cluster_id(),
2124                            replica_id: cluster_replica.replica_id(),
2125                        })
2126                    }
2127                    Err(e) => {
2128                        self.status = Err(e.into());
2129                        ResolvedObjectName::ClusterReplica(ResolvedClusterReplicaName {
2130                            // The ID is arbitrary here; we just need some dummy
2131                            // value to return.
2132                            cluster_id: ClusterId::system(0).expect("0 is a valid ID"),
2133                            replica_id: ReplicaId::System(0),
2134                        })
2135                    }
2136                }
2137            }
2138            UnresolvedObjectName::Database(name) => {
2139                ResolvedObjectName::Database(self.fold_database_name(name))
2140            }
2141            UnresolvedObjectName::Schema(name) => {
2142                ResolvedObjectName::Schema(self.fold_schema_name(name))
2143            }
2144            UnresolvedObjectName::Role(name) => ResolvedObjectName::Role(self.fold_role_name(name)),
2145            UnresolvedObjectName::Item(name) => {
2146                ResolvedObjectName::Item(self.fold_item_name(RawItemName::Name(name)))
2147            }
2148            UnresolvedObjectName::NetworkPolicy(name) => ResolvedObjectName::NetworkPolicy(
2149                self.fold_network_policy_name(RawNetworkPolicyName::Unresolved(name)),
2150            ),
2151        }
2152    }
2153
2154    fn fold_function(
2155        &mut self,
2156        node: mz_sql_parser::ast::Function<Raw>,
2157    ) -> mz_sql_parser::ast::Function<Aug> {
2158        // Functions implemented as SQL statements can have very deeply nested
2159        // and recursive structures, so need the ability to grow the stack.
2160        mz_ore::stack::maybe_grow(|| {
2161            mz_sql_parser::ast::Function {
2162                name: self.resolve_item_name(
2163                    node.name,
2164                    // When resolving a function name, only function items should be
2165                    // considered.
2166                    ItemResolutionConfig {
2167                        functions: true,
2168                        types: false,
2169                        relations: false,
2170                    },
2171                ),
2172                args: self.fold_function_args(node.args),
2173                filter: node.filter.map(|expr| Box::new(self.fold_expr(*expr))),
2174                over: node.over.map(|over| self.fold_window_spec(over)),
2175                distinct: node.distinct,
2176            }
2177        })
2178    }
2179
2180    fn fold_table_factor(
2181        &mut self,
2182        node: mz_sql_parser::ast::TableFactor<Raw>,
2183    ) -> mz_sql_parser::ast::TableFactor<Aug> {
2184        use mz_sql_parser::ast::TableFactor::*;
2185        match node {
2186            Table { name, alias } => Table {
2187                name: self.fold_item_name(name),
2188                alias: alias.map(|alias| self.fold_table_alias(alias)),
2189            },
2190            Function {
2191                function,
2192                alias,
2193                with_ordinality,
2194            } => {
2195                match &function.name {
2196                    RawItemName::Name(name) => {
2197                        if *name == UnresolvedItemName::unqualified(ident!("values"))
2198                            && self.status.is_ok()
2199                        {
2200                            self.status = Err(PlanError::FromValueRequiresParen);
2201                        }
2202                    }
2203                    RawItemName::Id(..) => {}
2204                }
2205
2206                Function {
2207                    function: self.fold_function(function),
2208                    alias: alias.map(|alias| self.fold_table_alias(alias)),
2209                    with_ordinality,
2210                }
2211            }
2212            RowsFrom {
2213                functions,
2214                alias,
2215                with_ordinality,
2216            } => RowsFrom {
2217                functions: functions
2218                    .into_iter()
2219                    .map(|f| self.fold_function(f))
2220                    .collect(),
2221                alias: alias.map(|alias| self.fold_table_alias(alias)),
2222                with_ordinality,
2223            },
2224            Derived {
2225                lateral,
2226                subquery,
2227                alias,
2228            } => Derived {
2229                lateral,
2230                subquery: Box::new(self.fold_query(*subquery)),
2231                alias: alias.map(|alias| self.fold_table_alias(alias)),
2232            },
2233            NestedJoin { join, alias } => NestedJoin {
2234                join: Box::new(self.fold_table_with_joins(*join)),
2235                alias: alias.map(|alias| self.fold_table_alias(alias)),
2236            },
2237        }
2238    }
2239
2240    fn fold_grant_target_specification(
2241        &mut self,
2242        node: GrantTargetSpecification<Raw>,
2243    ) -> GrantTargetSpecification<Aug> {
2244        match node {
2245            GrantTargetSpecification::Object {
2246                object_type: ObjectType::Type,
2247                object_spec_inner: GrantTargetSpecificationInner::Objects { names },
2248            } => GrantTargetSpecification::Object {
2249                object_type: ObjectType::Type,
2250                object_spec_inner: GrantTargetSpecificationInner::Objects {
2251                    names: names
2252                        .into_iter()
2253                        .map(|name| match name {
2254                            UnresolvedObjectName::Item(name) => {
2255                                ResolvedObjectName::Item(self.resolve_item_name_name(
2256                                    name,
2257                                    // `{GRANT|REVOKE} ... ON TYPE ...` can only
2258                                    // refer to type names.
2259                                    ItemResolutionConfig {
2260                                        functions: false,
2261                                        types: true,
2262                                        relations: false,
2263                                    },
2264                                ))
2265                            }
2266                            _ => self.fold_object_name(name),
2267                        })
2268                        .collect(),
2269                },
2270            },
2271            _ => mz_sql_parser::ast::fold::fold_grant_target_specification(self, node),
2272        }
2273    }
2274
2275    fn fold_doc_on_identifier(&mut self, node: DocOnIdentifier<Raw>) -> DocOnIdentifier<Aug> {
2276        match node {
2277            DocOnIdentifier::Column(name) => DocOnIdentifier::Column(self.fold_column_name(name)),
2278            DocOnIdentifier::Type(name) => DocOnIdentifier::Type(self.resolve_item_name(
2279                name,
2280                // In `DOC ON TYPE ...`, the type can refer to either a type or
2281                // a relation.
2282                //
2283                // It's possible this will get simpler once database-issues#7142 is fixed. See
2284                // the comment on `ItemResolutionConfig` for details.
2285                ItemResolutionConfig {
2286                    functions: false,
2287                    types: true,
2288                    relations: true,
2289                },
2290            )),
2291        }
2292    }
2293
2294    fn fold_expr(&mut self, node: Expr<Raw>) -> Expr<Aug> {
2295        // Exprs can be recursive, so need the ability to grow the stack.
2296        mz_ore::stack::maybe_grow(|| mz_sql_parser::ast::fold::fold_expr(self, node))
2297    }
2298}
2299
2300/// Resolves names in an AST node using the provided catalog.
2301#[mz_ore::instrument(target = "compiler", level = "trace", name = "ast_resolve_names")]
2302pub fn resolve<N>(
2303    catalog: &dyn SessionCatalog,
2304    node: N,
2305) -> Result<(N::Folded, ResolvedIds), PlanError>
2306where
2307    N: FoldNode<Raw, Aug>,
2308{
2309    let mut resolver = NameResolver::new(catalog);
2310    let result = node.fold(&mut resolver);
2311    resolver.status?;
2312    Ok((result, ResolvedIds::new(resolver.ids)))
2313}
2314
2315/// A set of items and their corresponding collections resolved by name resolution.
2316///
2317/// This is a newtype of a [`BTreeMap`] that is provided to make it harder to confuse a set of
2318/// resolved IDs with other collections of [`CatalogItemId`].
2319#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2320pub struct ResolvedIds {
2321    #[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
2322    entries: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>,
2323}
2324
2325impl ResolvedIds {
2326    fn new(entries: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>) -> Self {
2327        ResolvedIds { entries }
2328    }
2329
2330    /// Returns an emptry [`ResolvedIds`].
2331    pub fn empty() -> Self {
2332        ResolvedIds {
2333            entries: BTreeMap::new(),
2334        }
2335    }
2336
2337    /// Returns if the set of IDs is empty.
2338    pub fn is_empty(&self) -> bool {
2339        self.entries.is_empty()
2340    }
2341
2342    /// Returns all of the [`GlobalId`]s in this set.
2343    pub fn collections(&self) -> impl Iterator<Item = &GlobalId> {
2344        self.entries.values().flat_map(|gids| gids.into_iter())
2345    }
2346
2347    /// Returns all of the [`CatalogItemId`]s in this set.
2348    pub fn items(&self) -> impl Iterator<Item = &CatalogItemId> {
2349        self.entries.keys()
2350    }
2351
2352    /// Returns if this set of IDs contains the provided [`CatalogItemId`].
2353    pub fn contains_item(&self, item: &CatalogItemId) -> bool {
2354        self.entries.contains_key(item)
2355    }
2356
2357    pub fn add_item(&mut self, item: CatalogItemId) {
2358        self.entries.insert(item, BTreeSet::new());
2359    }
2360
2361    pub fn remove_item(&mut self, item: &CatalogItemId) {
2362        self.entries.remove(item);
2363    }
2364
2365    /// Merges all entries from `other` into `self`.
2366    pub fn extend_from(&mut self, other: &ResolvedIds) {
2367        for (id, gids) in &other.entries {
2368            self.entries
2369                .entry(*id)
2370                .or_default()
2371                .extend(gids.iter().copied());
2372        }
2373    }
2374
2375    /// Create a new [`ResolvedIds`] that contains the elements from `self`
2376    /// where `predicate` returns `true`.
2377    pub fn retain_items<F>(&self, predicate: F) -> Self
2378    where
2379        F: Fn(&CatalogItemId) -> bool,
2380    {
2381        let mut new_ids = self.clone();
2382        new_ids
2383            .entries
2384            .retain(|item_id, _global_ids| predicate(item_id));
2385        new_ids
2386    }
2387}
2388
2389impl FromIterator<(CatalogItemId, GlobalId)> for ResolvedIds {
2390    fn from_iter<T: IntoIterator<Item = (CatalogItemId, GlobalId)>>(iter: T) -> Self {
2391        let mut ids = ResolvedIds::empty();
2392        ids.extend(iter);
2393        ids
2394    }
2395}
2396
2397impl Extend<(CatalogItemId, GlobalId)> for ResolvedIds {
2398    fn extend<T: IntoIterator<Item = (CatalogItemId, GlobalId)>>(&mut self, iter: T) {
2399        for (item_id, global_id) in iter {
2400            self.entries.entry(item_id).or_default().insert(global_id);
2401        }
2402    }
2403}
2404
2405/// A set of IDs references by the `HirRelationExpr` of an object.
2406#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2407pub struct DependencyIds(pub BTreeSet<CatalogItemId>);
2408
2409impl FromIterator<CatalogItemId> for DependencyIds {
2410    fn from_iter<T: IntoIterator<Item = CatalogItemId>>(iter: T) -> Self {
2411        DependencyIds(iter.into_iter().collect())
2412    }
2413}
2414
2415#[derive(Debug)]
2416pub struct DependencyVisitor<'a> {
2417    catalog: &'a dyn SessionCatalog,
2418    ids: BTreeMap<CatalogItemId, BTreeSet<GlobalId>>,
2419}
2420
2421impl<'a> DependencyVisitor<'a> {
2422    pub fn new(catalog: &'a dyn SessionCatalog) -> Self {
2423        DependencyVisitor {
2424            catalog,
2425            ids: Default::default(),
2426        }
2427    }
2428}
2429
2430impl<'a, 'ast> Visit<'ast, Aug> for DependencyVisitor<'a> {
2431    fn visit_item_name(&mut self, item_name: &'ast <Aug as AstInfo>::ItemName) {
2432        if let ResolvedItemName::Item { id, version, .. } = item_name {
2433            let global_ids = self.ids.entry(*id).or_default();
2434            if let Some(item) = self.catalog.try_get_item(id) {
2435                global_ids.insert(item.at_version(*version).global_id());
2436            }
2437        }
2438    }
2439
2440    fn visit_data_type(&mut self, data_type: &'ast <Aug as AstInfo>::DataType) {
2441        match data_type {
2442            ResolvedDataType::AnonymousList(data_type) => self.visit_data_type(data_type),
2443            ResolvedDataType::AnonymousMap {
2444                key_type,
2445                value_type,
2446            } => {
2447                self.visit_data_type(key_type);
2448                self.visit_data_type(value_type);
2449            }
2450            ResolvedDataType::Named { id, .. } => {
2451                self.ids.entry(*id).or_default();
2452            }
2453            ResolvedDataType::Error => {}
2454        }
2455    }
2456}
2457
2458pub fn visit_dependencies<'ast, N>(catalog: &dyn SessionCatalog, node: &'ast N) -> ResolvedIds
2459where
2460    N: VisitNode<'ast, Aug> + 'ast,
2461{
2462    let mut visitor = DependencyVisitor::new(catalog);
2463    node.visit(&mut visitor);
2464    ResolvedIds::new(visitor.ids)
2465}
2466
2467#[derive(Debug)]
2468pub struct ItemDependencyModifier<'a> {
2469    pub modified: bool,
2470    pub id_map: &'a BTreeMap<CatalogItemId, CatalogItemId>,
2471}
2472
2473impl<'ast, 'a> VisitMut<'ast, Raw> for ItemDependencyModifier<'a> {
2474    fn visit_item_name_mut(&mut self, item_name: &mut RawItemName) {
2475        if let RawItemName::Id(id, _, _) = item_name {
2476            let parsed_id = id.parse::<CatalogItemId>().unwrap();
2477            if let Some(new_id) = self.id_map.get(&parsed_id) {
2478                *id = new_id.to_string();
2479                self.modified = true;
2480            }
2481        }
2482    }
2483}
2484
2485/// Updates any references in the provided AST node that are keys in `id_map`.
2486/// If an id is found it will be updated to the value of the key in `id_map`.
2487/// This assumes the names of the reference(s) are unmodified (e.g. each pair of
2488/// ids refer to an item of the same name, whose id has changed).
2489pub fn modify_dependency_item_ids<'ast, N>(
2490    node: &'ast mut N,
2491    id_map: &BTreeMap<CatalogItemId, CatalogItemId>,
2492) -> bool
2493where
2494    N: VisitMutNode<'ast, Raw>,
2495{
2496    let mut modifier = ItemDependencyModifier {
2497        id_map,
2498        modified: false,
2499    };
2500    node.visit_mut(&mut modifier);
2501
2502    modifier.modified
2503}
2504
2505// Used when displaying a view's source for human creation. If the name
2506// specified is the same as the name in the catalog, we don't use the ID format.
2507#[derive(Debug)]
2508pub struct NameSimplifier<'a> {
2509    pub catalog: &'a dyn SessionCatalog,
2510}
2511
2512impl<'ast, 'a> VisitMut<'ast, Aug> for NameSimplifier<'a> {
2513    fn visit_cluster_name_mut(&mut self, node: &mut ResolvedClusterName) {
2514        node.print_name = Some(self.catalog.get_cluster(node.id).name().into());
2515    }
2516
2517    fn visit_item_name_mut(&mut self, name: &mut ResolvedItemName) {
2518        if let ResolvedItemName::Item {
2519            id,
2520            full_name,
2521            print_id,
2522            ..
2523        } = name
2524        {
2525            let item = self.catalog.get_item(id);
2526            let catalog_full_name = self.catalog.resolve_full_name(item.name());
2527            if catalog_full_name == *full_name {
2528                *print_id = false;
2529            }
2530        }
2531    }
2532
2533    fn visit_data_type_mut(&mut self, name: &mut ResolvedDataType) {
2534        if let ResolvedDataType::Named {
2535            id,
2536            full_name,
2537            print_id,
2538            ..
2539        } = name
2540        {
2541            let item = self.catalog.get_item(id);
2542            let catalog_full_name = self.catalog.resolve_full_name(item.name());
2543            if catalog_full_name == *full_name {
2544                *print_id = false;
2545            }
2546        }
2547    }
2548}
2549
2550/// Returns the [`CatalogItemId`] dependencies the provided `node` has.
2551///
2552/// _DOES NOT_ resolve names, simply does a recursive walk through an object to
2553/// find all of the IDs.
2554pub fn dependencies<'ast, N>(node: &'ast N) -> Result<BTreeSet<CatalogItemId>, anyhow::Error>
2555where
2556    N: VisitNode<'ast, Raw>,
2557{
2558    let mut visitor = IdDependencVisitor::default();
2559    node.visit(&mut visitor);
2560    match visitor.error {
2561        Some(error) => Err(error),
2562        None => Ok(visitor.ids),
2563    }
2564}
2565
2566#[derive(Debug, Default)]
2567struct IdDependencVisitor {
2568    ids: BTreeSet<CatalogItemId>,
2569    error: Option<anyhow::Error>,
2570}
2571
2572impl<'ast> Visit<'ast, Raw> for IdDependencVisitor {
2573    fn visit_item_name(&mut self, node: &'ast <Raw as AstInfo>::ItemName) {
2574        // Bail early if we're already in an error state.
2575        if self.error.is_some() {
2576            return;
2577        }
2578
2579        match node {
2580            // Nothing to do! We don't lookup names.
2581            RawItemName::Name(_) => (),
2582            RawItemName::Id(id, _name, _version) => match id.parse::<CatalogItemId>() {
2583                Ok(id) => {
2584                    self.ids.insert(id);
2585                }
2586                Err(e) => {
2587                    self.error = Some(e);
2588                }
2589            },
2590        }
2591    }
2592}
2593
2594#[cfg(test)]
2595mod tests {
2596    use proptest::prelude::*;
2597
2598    use super::*;
2599
2600    #[mz_ore::test]
2601    fn proptest_schema_id_roundtrips() {
2602        fn testcase(og: SchemaId) {
2603            let s = og.to_string();
2604            let rnd: SchemaId = s.parse().unwrap();
2605            assert_eq!(og, rnd);
2606        }
2607
2608        proptest!(|(id in any::<SchemaId>())| {
2609            testcase(id);
2610        })
2611    }
2612
2613    #[mz_ore::test]
2614    fn proptest_database_id_roundtrips() {
2615        fn testcase(og: DatabaseId) {
2616            let s = og.to_string();
2617            let rnd: DatabaseId = s.parse().unwrap();
2618            assert_eq!(og, rnd);
2619        }
2620
2621        proptest!(|(id in any::<DatabaseId>())| {
2622            testcase(id);
2623        })
2624    }
2625
2626    #[mz_ore::test]
2627    fn test_schema_id_from_str() {
2628        assert_eq!("s5".parse::<SchemaId>().unwrap(), SchemaId::System(5));
2629        assert_eq!("u5".parse::<SchemaId>().unwrap(), SchemaId::User(5));
2630
2631        // Regression test for a panic on multi-byte leading characters, where
2632        // slicing off a single byte landed inside a UTF-8 char boundary (SQL-195).
2633        for invalid in ["ü1", "ü", "é42", "🦀7", "", "x1", "s"] {
2634            assert!(
2635                invalid.parse::<SchemaId>().is_err(),
2636                "expected {invalid:?} to fail to parse"
2637            );
2638        }
2639    }
2640
2641    #[mz_ore::test]
2642    fn test_database_id_from_str() {
2643        assert_eq!("s5".parse::<DatabaseId>().unwrap(), DatabaseId::System(5));
2644        assert_eq!("u5".parse::<DatabaseId>().unwrap(), DatabaseId::User(5));
2645
2646        // Regression test for a panic on multi-byte leading characters, where
2647        // slicing off a single byte landed inside a UTF-8 char boundary (SQL-195).
2648        for invalid in ["ü1", "ü", "é42", "🦀7", "", "x1", "u"] {
2649            assert!(
2650                invalid.parse::<DatabaseId>().is_err(),
2651                "expected {invalid:?} to fail to parse"
2652            );
2653        }
2654    }
2655}