Skip to main content

mz_sql/
names.rs

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