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