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