Skip to main content

mz_sql/
names.rs

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