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