Skip to main content

mz_sql_parser/ast/defs/
statement.rs

1// Copyright 2018 sqlparser-rs contributors. All rights reserved.
2// Copyright Materialize, Inc. and contributors. All rights reserved.
3//
4// This file is derived from the sqlparser-rs project, available at
5// https://github.com/andygrove/sqlparser-rs. It was incorporated
6// directly into Materialize on December 21, 2019.
7//
8// Licensed under the Apache License, Version 2.0 (the "License");
9// you may not use this file except in compliance with the License.
10// You may obtain a copy of the License in the LICENSE file at the
11// root of this repository, or online at
12//
13//     http://www.apache.org/licenses/LICENSE-2.0
14//
15// Unless required by applicable law or agreed to in writing, software
16// distributed under the License is distributed on an "AS IS" BASIS,
17// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18// See the License for the specific language governing permissions and
19// limitations under the License.
20
21use std::collections::BTreeMap;
22use std::fmt;
23
24use enum_kinds::EnumKind;
25use serde::{Deserialize, Serialize};
26use smallvec::{SmallVec, smallvec};
27
28use crate::ast::display::{self, AstDisplay, AstFormatter, WithOptionName};
29use crate::ast::{
30    AstInfo, ColumnDef, ConnectionOption, ConnectionOptionName, CreateConnectionOption,
31    CreateConnectionType, CreateSinkConnection, CreateSourceConnection, CreateSourceOption,
32    CreateSourceOptionName, DeferredItemName, Expr, Format, FormatSpecifier, IcebergSinkMode,
33    Ident, IntervalValue, KeyConstraint, MaterializedViewOption, Query, SelectItem, SinkEnvelope,
34    SourceEnvelope, SourceIncludeMetadata, SubscribeOutput, TableAlias, TableConstraint,
35    TableWithJoins, UnresolvedDatabaseName, UnresolvedItemName, UnresolvedObjectName,
36    UnresolvedSchemaName, Value,
37};
38
39/// A top-level statement (SELECT, INSERT, CREATE, etc.)
40#[allow(clippy::large_enum_variant)]
41#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumKind)]
42#[enum_kind(StatementKind, derive(Serialize, Deserialize))]
43pub enum Statement<T: AstInfo> {
44    Select(SelectStatement<T>),
45    Insert(InsertStatement<T>),
46    Copy(CopyStatement<T>),
47    Update(UpdateStatement<T>),
48    Delete(DeleteStatement<T>),
49    CreateConnection(CreateConnectionStatement<T>),
50    CreateDatabase(CreateDatabaseStatement),
51    CreateSchema(CreateSchemaStatement),
52    CreateWebhookSource(CreateWebhookSourceStatement<T>),
53    CreateSource(CreateSourceStatement<T>),
54    CreateSubsource(CreateSubsourceStatement<T>),
55    CreateSink(CreateSinkStatement<T>),
56    CreateMetricSink(CreateMetricSinkStatement<T>),
57    CreateView(CreateViewStatement<T>),
58    CreateMaterializedView(CreateMaterializedViewStatement<T>),
59    CreateTable(CreateTableStatement<T>),
60    CreateTableFromSource(CreateTableFromSourceStatement<T>),
61    CreateIndex(CreateIndexStatement<T>),
62    CreateType(CreateTypeStatement<T>),
63    CreateRole(CreateRoleStatement),
64    CreateCluster(CreateClusterStatement<T>),
65    CreateClusterReplica(CreateClusterReplicaStatement<T>),
66    CreateSecret(CreateSecretStatement<T>),
67    CreateNetworkPolicy(CreateNetworkPolicyStatement<T>),
68    AlterCluster(AlterClusterStatement<T>),
69    AlterOwner(AlterOwnerStatement<T>),
70    AlterObjectRename(AlterObjectRenameStatement),
71    AlterObjectSwap(AlterObjectSwapStatement),
72    AlterRetainHistory(AlterRetainHistoryStatement<T>),
73    AlterIndex(AlterIndexStatement<T>),
74    AlterSecret(AlterSecretStatement<T>),
75    AlterSetCluster(AlterSetClusterStatement<T>),
76    AlterSink(AlterSinkStatement<T>),
77    AlterSource(AlterSourceStatement<T>),
78    AlterSystemSet(AlterSystemSetStatement),
79    AlterSystemReset(AlterSystemResetStatement),
80    AlterSystemResetAll(AlterSystemResetAllStatement),
81    AlterConnection(AlterConnectionStatement<T>),
82    AlterNetworkPolicy(AlterNetworkPolicyStatement<T>),
83    AlterRole(AlterRoleStatement<T>),
84    AlterTableAddColumn(AlterTableAddColumnStatement<T>),
85    AlterMaterializedViewApplyReplacement(AlterMaterializedViewApplyReplacementStatement),
86    Discard(DiscardStatement),
87    DropObjects(DropObjectsStatement),
88    DropOwned(DropOwnedStatement<T>),
89    SetVariable(SetVariableStatement),
90    ResetVariable(ResetVariableStatement),
91    Show(ShowStatement<T>),
92    StartTransaction(StartTransactionStatement),
93    SetTransaction(SetTransactionStatement),
94    Commit(CommitStatement),
95    Rollback(RollbackStatement),
96    Subscribe(SubscribeStatement<T>),
97    ExplainPlan(ExplainPlanStatement<T>),
98    ExplainPushdown(ExplainPushdownStatement<T>),
99    ExplainTimestamp(ExplainTimestampStatement<T>),
100    ExplainSinkSchema(ExplainSinkSchemaStatement<T>),
101    ExplainAnalyzeObject(ExplainAnalyzeObjectStatement<T>),
102    ExplainAnalyzeCluster(ExplainAnalyzeClusterStatement),
103    Declare(DeclareStatement<T>),
104    Fetch(FetchStatement<T>),
105    Close(CloseStatement),
106    Prepare(PrepareStatement<T>),
107    Execute(ExecuteStatement<T>),
108    ExecuteUnitTest(ExecuteUnitTestStatement<T>),
109    Deallocate(DeallocateStatement),
110    Raise(RaiseStatement),
111    GrantRole(GrantRoleStatement<T>),
112    RevokeRole(RevokeRoleStatement<T>),
113    GrantPrivileges(GrantPrivilegesStatement<T>),
114    RevokePrivileges(RevokePrivilegesStatement<T>),
115    AlterDefaultPrivileges(AlterDefaultPrivilegesStatement<T>),
116    ReassignOwned(ReassignOwnedStatement<T>),
117    ValidateConnection(ValidateConnectionStatement<T>),
118    Comment(CommentStatement<T>),
119}
120
121impl<T: AstInfo> AstDisplay for Statement<T> {
122    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
123        match self {
124            Statement::Select(stmt) => f.write_node(stmt),
125            Statement::Insert(stmt) => f.write_node(stmt),
126            Statement::Copy(stmt) => f.write_node(stmt),
127            Statement::Update(stmt) => f.write_node(stmt),
128            Statement::Delete(stmt) => f.write_node(stmt),
129            Statement::CreateConnection(stmt) => f.write_node(stmt),
130            Statement::CreateDatabase(stmt) => f.write_node(stmt),
131            Statement::CreateSchema(stmt) => f.write_node(stmt),
132            Statement::CreateWebhookSource(stmt) => f.write_node(stmt),
133            Statement::CreateSource(stmt) => f.write_node(stmt),
134            Statement::CreateSubsource(stmt) => f.write_node(stmt),
135            Statement::CreateSink(stmt) => f.write_node(stmt),
136            Statement::CreateMetricSink(stmt) => f.write_node(stmt),
137            Statement::CreateView(stmt) => f.write_node(stmt),
138            Statement::CreateMaterializedView(stmt) => f.write_node(stmt),
139            Statement::CreateTable(stmt) => f.write_node(stmt),
140            Statement::CreateTableFromSource(stmt) => f.write_node(stmt),
141            Statement::CreateIndex(stmt) => f.write_node(stmt),
142            Statement::CreateRole(stmt) => f.write_node(stmt),
143            Statement::CreateSecret(stmt) => f.write_node(stmt),
144            Statement::CreateType(stmt) => f.write_node(stmt),
145            Statement::CreateCluster(stmt) => f.write_node(stmt),
146            Statement::CreateClusterReplica(stmt) => f.write_node(stmt),
147            Statement::CreateNetworkPolicy(stmt) => f.write_node(stmt),
148            Statement::AlterCluster(stmt) => f.write_node(stmt),
149            Statement::AlterNetworkPolicy(stmt) => f.write_node(stmt),
150            Statement::AlterOwner(stmt) => f.write_node(stmt),
151            Statement::AlterObjectRename(stmt) => f.write_node(stmt),
152            Statement::AlterRetainHistory(stmt) => f.write_node(stmt),
153            Statement::AlterObjectSwap(stmt) => f.write_node(stmt),
154            Statement::AlterIndex(stmt) => f.write_node(stmt),
155            Statement::AlterSetCluster(stmt) => f.write_node(stmt),
156            Statement::AlterSecret(stmt) => f.write_node(stmt),
157            Statement::AlterSink(stmt) => f.write_node(stmt),
158            Statement::AlterSource(stmt) => f.write_node(stmt),
159            Statement::AlterSystemSet(stmt) => f.write_node(stmt),
160            Statement::AlterSystemReset(stmt) => f.write_node(stmt),
161            Statement::AlterSystemResetAll(stmt) => f.write_node(stmt),
162            Statement::AlterConnection(stmt) => f.write_node(stmt),
163            Statement::AlterRole(stmt) => f.write_node(stmt),
164            Statement::AlterTableAddColumn(stmt) => f.write_node(stmt),
165            Statement::AlterMaterializedViewApplyReplacement(stmt) => f.write_node(stmt),
166            Statement::Discard(stmt) => f.write_node(stmt),
167            Statement::DropObjects(stmt) => f.write_node(stmt),
168            Statement::DropOwned(stmt) => f.write_node(stmt),
169            Statement::SetVariable(stmt) => f.write_node(stmt),
170            Statement::ResetVariable(stmt) => f.write_node(stmt),
171            Statement::Show(stmt) => f.write_node(stmt),
172            Statement::StartTransaction(stmt) => f.write_node(stmt),
173            Statement::SetTransaction(stmt) => f.write_node(stmt),
174            Statement::Commit(stmt) => f.write_node(stmt),
175            Statement::Rollback(stmt) => f.write_node(stmt),
176            Statement::Subscribe(stmt) => f.write_node(stmt),
177            Statement::ExplainPlan(stmt) => f.write_node(stmt),
178            Statement::ExplainPushdown(stmt) => f.write_node(stmt),
179            Statement::ExplainAnalyzeObject(stmt) => f.write_node(stmt),
180            Statement::ExplainAnalyzeCluster(stmt) => f.write_node(stmt),
181            Statement::ExplainTimestamp(stmt) => f.write_node(stmt),
182            Statement::ExplainSinkSchema(stmt) => f.write_node(stmt),
183            Statement::Declare(stmt) => f.write_node(stmt),
184            Statement::Close(stmt) => f.write_node(stmt),
185            Statement::Fetch(stmt) => f.write_node(stmt),
186            Statement::Prepare(stmt) => f.write_node(stmt),
187            Statement::Execute(stmt) => f.write_node(stmt),
188            Statement::ExecuteUnitTest(stmt) => f.write_node(stmt),
189            Statement::Deallocate(stmt) => f.write_node(stmt),
190            Statement::Raise(stmt) => f.write_node(stmt),
191            Statement::GrantRole(stmt) => f.write_node(stmt),
192            Statement::RevokeRole(stmt) => f.write_node(stmt),
193            Statement::GrantPrivileges(stmt) => f.write_node(stmt),
194            Statement::RevokePrivileges(stmt) => f.write_node(stmt),
195            Statement::AlterDefaultPrivileges(stmt) => f.write_node(stmt),
196            Statement::ReassignOwned(stmt) => f.write_node(stmt),
197            Statement::ValidateConnection(stmt) => f.write_node(stmt),
198            Statement::Comment(stmt) => f.write_node(stmt),
199        }
200    }
201}
202impl_display_t!(Statement);
203
204impl StatementKind {
205    /// Whether this kind of statement can carry secret values, i.e. `CREATE
206    /// SECRET` or `ALTER SECRET`. Such secret material must never be persisted
207    /// verbatim (e.g. in `mz_statement_execution_history`), not even in error
208    /// messages.
209    pub fn is_secret(&self) -> bool {
210        matches!(
211            self,
212            StatementKind::CreateSecret | StatementKind::AlterSecret
213        )
214    }
215
216    /// Whether this kind of statement can carry sensitive material that we
217    /// redact from logged SQL text (and error messages): secret values, or
218    /// bulk/PII user data in `INSERT`/`UPDATE`/`EXECUTE`. A superset of
219    /// [`Self::is_secret`].
220    pub fn is_sensitive(&self) -> bool {
221        self.is_secret()
222            || matches!(
223                self,
224                StatementKind::Insert | StatementKind::Update | StatementKind::Execute
225            )
226    }
227}
228
229/// A static str for each statement kind
230pub fn statement_kind_label_value(kind: StatementKind) -> &'static str {
231    match kind {
232        StatementKind::Select => "select",
233        StatementKind::Insert => "insert",
234        StatementKind::Copy => "copy",
235        StatementKind::Update => "update",
236        StatementKind::Delete => "delete",
237        StatementKind::CreateConnection => "create_connection",
238        StatementKind::CreateDatabase => "create_database",
239        StatementKind::CreateSchema => "create_schema",
240        StatementKind::CreateWebhookSource => "create_webhook",
241        StatementKind::CreateSource => "create_source",
242        StatementKind::CreateSubsource => "create_subsource",
243        StatementKind::CreateSink => "create_sink",
244        StatementKind::CreateMetricSink => "create_metric_sink",
245        StatementKind::CreateView => "create_view",
246        StatementKind::CreateMaterializedView => "create_materialized_view",
247        StatementKind::CreateTable => "create_table",
248        StatementKind::CreateTableFromSource => "create_table_from_source",
249        StatementKind::CreateIndex => "create_index",
250        StatementKind::CreateType => "create_type",
251        StatementKind::CreateRole => "create_role",
252        StatementKind::CreateCluster => "create_cluster",
253        StatementKind::CreateClusterReplica => "create_cluster_replica",
254        StatementKind::CreateSecret => "create_secret",
255        StatementKind::CreateNetworkPolicy => "create_network_policy",
256        StatementKind::AlterCluster => "alter_cluster",
257        StatementKind::AlterObjectRename => "alter_object_rename",
258        StatementKind::AlterRetainHistory => "alter_retain_history",
259        StatementKind::AlterObjectSwap => "alter_object_swap",
260        StatementKind::AlterIndex => "alter_index",
261        StatementKind::AlterNetworkPolicy => "alter_network_policy",
262        StatementKind::AlterRole => "alter_role",
263        StatementKind::AlterSecret => "alter_secret",
264        StatementKind::AlterSetCluster => "alter_set_cluster",
265        StatementKind::AlterSink => "alter_sink",
266        StatementKind::AlterSource => "alter_source",
267        StatementKind::AlterSystemSet => "alter_system_set",
268        StatementKind::AlterSystemReset => "alter_system_reset",
269        StatementKind::AlterSystemResetAll => "alter_system_reset_all",
270        StatementKind::AlterOwner => "alter_owner",
271        StatementKind::AlterConnection => "alter_connection",
272        StatementKind::AlterTableAddColumn => "alter_table",
273        StatementKind::AlterMaterializedViewApplyReplacement => {
274            "alter_materialized_view_apply_replacement"
275        }
276        StatementKind::Discard => "discard",
277        StatementKind::DropObjects => "drop_objects",
278        StatementKind::DropOwned => "drop_owned",
279        StatementKind::SetVariable => "set_variable",
280        StatementKind::ResetVariable => "reset_variable",
281        StatementKind::Show => "show",
282        StatementKind::StartTransaction => "start_transaction",
283        StatementKind::SetTransaction => "set_transaction",
284        StatementKind::Commit => "commit",
285        StatementKind::Rollback => "rollback",
286        StatementKind::Subscribe => "subscribe",
287        StatementKind::ExplainPlan => "explain_plan",
288        StatementKind::ExplainPushdown => "explain_pushdown",
289        StatementKind::ExplainAnalyzeObject => "explain_analyze_object",
290        StatementKind::ExplainAnalyzeCluster => "explain_analyze_cluster",
291        StatementKind::ExplainTimestamp => "explain_timestamp",
292        StatementKind::ExplainSinkSchema => "explain_sink_schema",
293        StatementKind::Declare => "declare",
294        StatementKind::Fetch => "fetch",
295        StatementKind::Close => "close",
296        StatementKind::Prepare => "prepare",
297        StatementKind::Execute => "execute",
298        StatementKind::ExecuteUnitTest => "execute_unit_test",
299        StatementKind::Deallocate => "deallocate",
300        StatementKind::Raise => "raise",
301        StatementKind::GrantRole => "grant_role",
302        StatementKind::RevokeRole => "revoke_role",
303        StatementKind::GrantPrivileges => "grant_privileges",
304        StatementKind::RevokePrivileges => "revoke_privileges",
305        StatementKind::AlterDefaultPrivileges => "alter_default_privileges",
306        StatementKind::ReassignOwned => "reassign_owned",
307        StatementKind::ValidateConnection => "validate_connection",
308        StatementKind::Comment => "comment",
309    }
310}
311
312/// `SELECT`
313#[derive(Debug, Clone, PartialEq, Eq, Hash)]
314pub struct SelectStatement<T: AstInfo> {
315    pub query: Query<T>,
316    pub as_of: Option<AsOf<T>>,
317}
318
319impl<T: AstInfo> AstDisplay for SelectStatement<T> {
320    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
321        // A query whose rendering begins with `SHOW` (a bare `SHOW` body, or a
322        // set operation whose leftmost operand is one) only reparses as a query
323        // when parenthesized: a top-level leading `SHOW` is dispatched as a
324        // `Statement::Show`, which terminates and would reject a following set
325        // operator or ORDER BY/LIMIT/OFFSET. The parser unwraps the redundant
326        // outer parens (see `parse_query_tail`), so wrapping round-trips.
327        let parenthesize_show = self.query.body.starts_with_show();
328        if parenthesize_show {
329            f.write_str("(");
330        }
331        f.write_node(&self.query);
332        if parenthesize_show {
333            f.write_str(")");
334        }
335        if let Some(as_of) = &self.as_of {
336            f.write_str(" ");
337            f.write_node(as_of);
338        }
339    }
340}
341impl_display_t!(SelectStatement);
342
343/// `INSERT`
344#[derive(Debug, Clone, PartialEq, Eq, Hash)]
345pub struct InsertStatement<T: AstInfo> {
346    /// TABLE
347    pub table_name: T::ItemName,
348    /// COLUMNS
349    pub columns: Vec<Ident>,
350    /// A SQL query that specifies what to insert.
351    pub source: InsertSource<T>,
352    /// RETURNING
353    pub returning: Vec<SelectItem<T>>,
354}
355
356impl<T: AstInfo> AstDisplay for InsertStatement<T> {
357    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
358        f.write_str("INSERT INTO ");
359        f.write_node(&self.table_name);
360        if !self.columns.is_empty() {
361            f.write_str(" (");
362            f.write_node(&display::comma_separated(&self.columns));
363            f.write_str(")");
364        }
365        f.write_str(" ");
366        f.write_node(&self.source);
367        if !self.returning.is_empty() {
368            f.write_str(" RETURNING ");
369            f.write_node(&display::comma_separated(&self.returning));
370        }
371    }
372}
373impl_display_t!(InsertStatement);
374
375#[derive(Debug, Clone, PartialEq, Eq, Hash)]
376pub enum CopyRelation<T: AstInfo> {
377    Named {
378        name: T::ItemName,
379        columns: Vec<Ident>,
380    },
381    Select(SelectStatement<T>),
382    Subscribe(SubscribeStatement<T>),
383}
384
385#[derive(Debug, Clone, PartialEq, Eq, Hash)]
386pub enum CopyDirection {
387    To,
388    From,
389}
390
391impl AstDisplay for CopyDirection {
392    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
393        f.write_str(match self {
394            CopyDirection::To => "TO",
395            CopyDirection::From => "FROM",
396        })
397    }
398}
399impl_display!(CopyDirection);
400
401#[derive(Debug, Clone, PartialEq, Eq, Hash)]
402pub enum CopyTarget<T: AstInfo> {
403    Stdin,
404    Stdout,
405    Expr(Expr<T>),
406}
407
408impl<T: AstInfo> AstDisplay for CopyTarget<T> {
409    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
410        match self {
411            CopyTarget::Stdin => f.write_str("STDIN"),
412            CopyTarget::Stdout => f.write_str("STDOUT"),
413            CopyTarget::Expr(expr) => f.write_node(expr),
414        }
415    }
416}
417impl_display_t!(CopyTarget);
418
419#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
420pub enum CopyOptionName {
421    Format,
422    Delimiter,
423    Null,
424    Escape,
425    Quote,
426    Header,
427    AwsConnection,
428    MaxFileSize,
429    Files,
430    Pattern,
431}
432
433impl AstDisplay for CopyOptionName {
434    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
435        f.write_str(match self {
436            CopyOptionName::Format => "FORMAT",
437            CopyOptionName::Delimiter => "DELIMITER",
438            CopyOptionName::Null => "NULL",
439            CopyOptionName::Escape => "ESCAPE",
440            CopyOptionName::Quote => "QUOTE",
441            CopyOptionName::Header => "HEADER",
442            CopyOptionName::AwsConnection => "AWS CONNECTION",
443            CopyOptionName::MaxFileSize => "MAX FILE SIZE",
444            CopyOptionName::Files => "FILES",
445            CopyOptionName::Pattern => "PATTERN",
446        })
447    }
448}
449
450impl WithOptionName for CopyOptionName {
451    /// # WARNING
452    ///
453    /// Whenever implementing this trait consider very carefully whether or not
454    /// this value could contain sensitive user data. If you're uncertain, err
455    /// on the conservative side and return `true`.
456    fn redact_value(&self) -> bool {
457        match self {
458            CopyOptionName::Format
459            | CopyOptionName::Delimiter
460            | CopyOptionName::Null
461            | CopyOptionName::Escape
462            | CopyOptionName::Quote
463            | CopyOptionName::Header
464            | CopyOptionName::AwsConnection
465            | CopyOptionName::MaxFileSize => false,
466            CopyOptionName::Files | CopyOptionName::Pattern => true,
467        }
468    }
469}
470
471#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
472pub struct CopyOption<T: AstInfo> {
473    pub name: CopyOptionName,
474    pub value: Option<WithOptionValue<T>>,
475}
476impl_display_for_with_option!(CopyOption);
477
478/// `COPY`
479#[derive(Debug, Clone, PartialEq, Eq, Hash)]
480pub struct CopyStatement<T: AstInfo> {
481    /// RELATION
482    pub relation: CopyRelation<T>,
483    /// DIRECTION
484    pub direction: CopyDirection,
485    // TARGET
486    pub target: CopyTarget<T>,
487    // OPTIONS
488    pub options: Vec<CopyOption<T>>,
489}
490
491impl<T: AstInfo> AstDisplay for CopyStatement<T> {
492    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
493        f.write_str("COPY ");
494        match &self.relation {
495            CopyRelation::Named { name, columns } => {
496                f.write_node(name);
497                if !columns.is_empty() {
498                    f.write_str("(");
499                    f.write_node(&display::comma_separated(columns));
500                    f.write_str(")");
501                }
502            }
503            CopyRelation::Select(query) => {
504                f.write_str("(");
505                f.write_node(query);
506                f.write_str(")");
507            }
508            CopyRelation::Subscribe(query) => {
509                f.write_str("(");
510                f.write_node(query);
511                f.write_str(")");
512            }
513        };
514        f.write_str(" ");
515        f.write_node(&self.direction);
516        f.write_str(" ");
517        f.write_node(&self.target);
518        if !self.options.is_empty() {
519            f.write_str(" WITH (");
520            f.write_node(&display::comma_separated(&self.options));
521            f.write_str(")");
522        }
523    }
524}
525impl_display_t!(CopyStatement);
526
527/// `UPDATE`
528#[derive(Debug, Clone, PartialEq, Eq, Hash)]
529pub struct UpdateStatement<T: AstInfo> {
530    /// `FROM`
531    pub table_name: T::ItemName,
532    pub alias: Option<TableAlias>,
533    /// Column assignments
534    pub assignments: Vec<Assignment<T>>,
535    /// WHERE
536    pub selection: Option<Expr<T>>,
537}
538
539impl<T: AstInfo> AstDisplay for UpdateStatement<T> {
540    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
541        f.write_str("UPDATE ");
542        f.write_node(&self.table_name);
543        if let Some(alias) = &self.alias {
544            f.write_str(" AS ");
545            f.write_node(alias);
546        }
547        if !self.assignments.is_empty() {
548            f.write_str(" SET ");
549            f.write_node(&display::comma_separated(&self.assignments));
550        }
551        if let Some(selection) = &self.selection {
552            f.write_str(" WHERE ");
553            f.write_node(selection);
554        }
555    }
556}
557impl_display_t!(UpdateStatement);
558
559/// `DELETE`
560#[derive(Debug, Clone, PartialEq, Eq, Hash)]
561pub struct DeleteStatement<T: AstInfo> {
562    /// `FROM`
563    pub table_name: T::ItemName,
564    /// `AS`
565    pub alias: Option<TableAlias>,
566    /// `USING`
567    pub using: Vec<TableWithJoins<T>>,
568    /// `WHERE`
569    pub selection: Option<Expr<T>>,
570}
571
572impl<T: AstInfo> AstDisplay for DeleteStatement<T> {
573    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
574        f.write_str("DELETE FROM ");
575        f.write_node(&self.table_name);
576        if let Some(alias) = &self.alias {
577            f.write_str(" AS ");
578            f.write_node(alias);
579        }
580        if !self.using.is_empty() {
581            f.write_str(" USING ");
582            f.write_node(&display::comma_separated(&self.using));
583        }
584        if let Some(selection) = &self.selection {
585            f.write_str(" WHERE ");
586            f.write_node(selection);
587        }
588    }
589}
590impl_display_t!(DeleteStatement);
591
592/// `CREATE DATABASE`
593#[derive(Debug, Clone, PartialEq, Eq, Hash)]
594pub struct CreateDatabaseStatement {
595    pub name: UnresolvedDatabaseName,
596    pub if_not_exists: bool,
597}
598
599impl AstDisplay for CreateDatabaseStatement {
600    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
601        f.write_str("CREATE DATABASE ");
602        if self.if_not_exists {
603            f.write_str("IF NOT EXISTS ");
604        }
605        f.write_node(&self.name);
606    }
607}
608impl_display!(CreateDatabaseStatement);
609
610/// `CREATE SCHEMA`
611#[derive(Debug, Clone, PartialEq, Eq, Hash)]
612pub struct CreateSchemaStatement {
613    pub name: UnresolvedSchemaName,
614    pub if_not_exists: bool,
615}
616
617impl AstDisplay for CreateSchemaStatement {
618    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
619        f.write_str("CREATE SCHEMA ");
620        if self.if_not_exists {
621            f.write_str("IF NOT EXISTS ");
622        }
623        f.write_node(&self.name);
624    }
625}
626impl_display!(CreateSchemaStatement);
627
628#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
629pub struct ConnectionDefaultAwsPrivatelink<T: AstInfo> {
630    pub connection: T::ItemName,
631    // TODO port should be switched to a vec of options similar to KafkaBrokerAwsPrivatelink if ever support more than port
632    pub port: Option<u16>,
633}
634
635impl<T: AstInfo> AstDisplay for ConnectionDefaultAwsPrivatelink<T> {
636    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
637        f.write_node(&self.connection);
638        if let Some(port) = self.port {
639            f.write_str(" (PORT ");
640            f.write_node(&display::escape_single_quote_string(&port.to_string()));
641            f.write_str(")");
642        }
643    }
644}
645impl_display_t!(ConnectionDefaultAwsPrivatelink);
646
647#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
648/// A MATCHING rule inside BROKERS (...) that routes brokers matching a pattern
649/// through an AWS PrivateLink tunnel.
650pub struct KafkaMatchingBrokerRule<T: AstInfo> {
651    /// Given a broker's host:port, should we use this route?
652    pub pattern: ConnectionRulePattern,
653    /// Route to the broker through this PrivateLink connection.
654    pub tunnel: KafkaBrokerAwsPrivatelink<T>,
655}
656
657#[derive(
658    Debug,
659    Clone,
660    PartialEq,
661    Eq,
662    Hash,
663    PartialOrd,
664    Ord,
665    Serialize,
666    Deserialize
667)]
668/// Parsed from a string, with optional leading and trailing '*' wildcards.
669pub struct ConnectionRulePattern {
670    /// If true, allow any combination of characters before the literal match.
671    pub prefix_wildcard: bool,
672    /// We expect the broker's host:port to match these characters in their entirety.
673    pub literal_match: String,
674    /// If true, allow any combination of characters after the literal match.
675    pub suffix_wildcard: bool,
676}
677
678impl<T: AstInfo> AstDisplay for KafkaMatchingBrokerRule<T> {
679    fn fmt<W>(&self, f: &mut AstFormatter<W>)
680    where
681        W: fmt::Write,
682    {
683        f.write_str("MATCHING ");
684        f.write_node(&self.pattern);
685        f.write_str(" ");
686        f.write_node(&self.tunnel);
687    }
688}
689impl_display_t!(KafkaMatchingBrokerRule);
690
691impl AstDisplay for ConnectionRulePattern {
692    fn fmt<W>(&self, f: &mut AstFormatter<W>)
693    where
694        W: fmt::Write,
695    {
696        f.write_str("'");
697        if self.prefix_wildcard {
698            f.write_str("*");
699        }
700        f.write_node(&display::escape_single_quote_string(&self.literal_match));
701        if self.suffix_wildcard {
702            f.write_str("*");
703        }
704        f.write_str("'");
705    }
706}
707impl_display!(ConnectionRulePattern);
708
709#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
710pub struct KafkaBroker<T: AstInfo> {
711    pub address: String,
712    pub tunnel: KafkaBrokerTunnel<T>,
713}
714
715impl<T: AstInfo> AstDisplay for KafkaBroker<T> {
716    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
717        f.write_str("'");
718        f.write_node(&display::escape_single_quote_string(&self.address));
719        f.write_str("'");
720        f.write_node(&self.tunnel);
721    }
722}
723
724impl_display_t!(KafkaBroker);
725
726#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
727pub enum KafkaBrokerTunnel<T: AstInfo> {
728    Direct,
729    AwsPrivatelink(KafkaBrokerAwsPrivatelink<T>),
730    SshTunnel(T::ItemName),
731}
732
733impl<T: AstInfo> AstDisplay for KafkaBrokerTunnel<T> {
734    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
735        use KafkaBrokerTunnel::*;
736        match self {
737            Direct => {}
738            AwsPrivatelink(aws) => {
739                f.write_str(" ");
740                f.write_node(aws);
741            }
742            Self::SshTunnel(connection) => {
743                f.write_str("USING SSH TUNNEL ");
744                f.write_node(connection);
745            }
746        }
747    }
748}
749
750impl_display_t!(KafkaBrokerTunnel);
751
752#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
753pub enum KafkaBrokerAwsPrivatelinkOptionName {
754    AvailabilityZone,
755    Port,
756}
757
758impl AstDisplay for KafkaBrokerAwsPrivatelinkOptionName {
759    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
760        match self {
761            Self::AvailabilityZone => f.write_str("AVAILABILITY ZONE"),
762            Self::Port => f.write_str("PORT"),
763        }
764    }
765}
766impl_display!(KafkaBrokerAwsPrivatelinkOptionName);
767
768impl WithOptionName for KafkaBrokerAwsPrivatelinkOptionName {
769    /// # WARNING
770    ///
771    /// Whenever implementing this trait consider very carefully whether or not
772    /// this value could contain sensitive user data. If you're uncertain, err
773    /// on the conservative side and return `true`.
774    fn redact_value(&self) -> bool {
775        match self {
776            Self::AvailabilityZone | Self::Port => false,
777        }
778    }
779}
780
781#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
782pub struct KafkaBrokerAwsPrivatelinkOption<T: AstInfo> {
783    pub name: KafkaBrokerAwsPrivatelinkOptionName,
784    pub value: Option<WithOptionValue<T>>,
785}
786impl_display_for_with_option!(KafkaBrokerAwsPrivatelinkOption);
787impl_display_t!(KafkaBrokerAwsPrivatelinkOption);
788
789#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
790pub struct KafkaBrokerAwsPrivatelink<T: AstInfo> {
791    pub connection: T::ItemName,
792    pub options: Vec<KafkaBrokerAwsPrivatelinkOption<T>>,
793}
794
795impl<T: AstInfo> AstDisplay for KafkaBrokerAwsPrivatelink<T> {
796    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
797        f.write_str("USING AWS PRIVATELINK ");
798        f.write_node(&self.connection);
799        if !self.options.is_empty() {
800            f.write_str(" (");
801            f.write_node(&display::comma_separated(&self.options));
802            f.write_str(")");
803        }
804    }
805}
806impl_display_t!(KafkaBrokerAwsPrivatelink);
807
808/// `CREATE CONNECTION` refactor WIP
809#[derive(Debug, Clone, PartialEq, Eq, Hash)]
810pub struct CreateConnectionStatement<T: AstInfo> {
811    pub name: UnresolvedItemName,
812    pub connection_type: CreateConnectionType,
813    pub if_not_exists: bool,
814    pub values: Vec<ConnectionOption<T>>,
815    pub with_options: Vec<CreateConnectionOption<T>>,
816}
817
818impl<T: AstInfo> AstDisplay for CreateConnectionStatement<T> {
819    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
820        f.write_str("CREATE CONNECTION ");
821        if self.if_not_exists {
822            f.write_str("IF NOT EXISTS ");
823        }
824        f.write_node(&self.name);
825        f.write_str(" TO ");
826        self.connection_type.fmt(f);
827        f.write_str(" (");
828        f.write_node(&display::comma_separated(&self.values));
829        f.write_str(")");
830
831        if !self.with_options.is_empty() {
832            f.write_str(" WITH (");
833            f.write_node(&display::comma_separated(&self.with_options));
834            f.write_str(")");
835        }
836    }
837}
838impl_display_t!(CreateConnectionStatement);
839
840/// `VALIDATE CONNECTION`
841#[derive(Debug, Clone, PartialEq, Eq, Hash)]
842pub struct ValidateConnectionStatement<T: AstInfo> {
843    /// The connection to validate
844    pub name: T::ItemName,
845}
846
847impl<T: AstInfo> AstDisplay for ValidateConnectionStatement<T> {
848    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
849        f.write_str("VALIDATE CONNECTION ");
850        f.write_node(&self.name);
851    }
852}
853impl_display_t!(ValidateConnectionStatement);
854
855/// `CREATE (SOURCE | TABLE) <name> FROM WEBHOOK`
856#[derive(Debug, Clone, PartialEq, Eq, Hash)]
857pub struct CreateWebhookSourceStatement<T: AstInfo> {
858    pub name: UnresolvedItemName,
859    pub is_table: bool,
860    pub if_not_exists: bool,
861    pub body_format: Format<T>,
862    pub include_headers: CreateWebhookSourceIncludeHeaders,
863    pub validate_using: Option<CreateWebhookSourceCheck<T>>,
864    pub in_cluster: Option<T::ClusterName>,
865}
866
867impl<T: AstInfo> AstDisplay for CreateWebhookSourceStatement<T> {
868    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
869        f.write_str("CREATE ");
870
871        if self.is_table {
872            f.write_str("TABLE ");
873        } else {
874            f.write_str("SOURCE ");
875        }
876
877        if self.if_not_exists {
878            f.write_str("IF NOT EXISTS ");
879        }
880        f.write_node(&self.name);
881
882        // CREATE TABLE ... FROM WEBHOOK does not support specifying a cluster.
883        if !self.is_table {
884            if let Some(cluster_name) = &self.in_cluster {
885                f.write_str(" IN CLUSTER ");
886                f.write_node(cluster_name);
887            }
888        }
889
890        f.write_str(" FROM WEBHOOK ");
891
892        f.write_str("BODY FORMAT ");
893        f.write_node(&self.body_format);
894
895        f.write_node(&self.include_headers);
896
897        if let Some(validate) = &self.validate_using {
898            f.write_str(" ");
899            f.write_node(validate);
900        }
901    }
902}
903
904impl_display_t!(CreateWebhookSourceStatement);
905
906/// `CHECK ( ... )`
907#[derive(Debug, Clone, PartialEq, Eq, Hash)]
908pub struct CreateWebhookSourceCheck<T: AstInfo> {
909    pub options: Option<CreateWebhookSourceCheckOptions<T>>,
910    pub using: Expr<T>,
911}
912
913impl<T: AstInfo> AstDisplay for CreateWebhookSourceCheck<T> {
914    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
915        f.write_str("CHECK (");
916
917        if let Some(options) = &self.options {
918            f.write_node(options);
919            f.write_str(" ");
920        }
921
922        f.write_node(&self.using);
923        f.write_str(")");
924    }
925}
926
927impl_display_t!(CreateWebhookSourceCheck);
928
929/// `CHECK ( WITH ( ... ) )`
930#[derive(Debug, Clone, PartialEq, Eq, Hash)]
931pub struct CreateWebhookSourceCheckOptions<T: AstInfo> {
932    pub secrets: Vec<CreateWebhookSourceSecret<T>>,
933    pub headers: Vec<CreateWebhookSourceHeader>,
934    pub bodies: Vec<CreateWebhookSourceBody>,
935}
936
937impl<T: AstInfo> AstDisplay for CreateWebhookSourceCheckOptions<T> {
938    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
939        f.write_str("WITH (");
940
941        let mut delim = "";
942        if !self.headers.is_empty() {
943            f.write_node(&display::comma_separated(&self.headers[..]));
944            delim = ", ";
945        }
946        if !self.bodies.is_empty() {
947            f.write_str(delim);
948            f.write_node(&display::comma_separated(&self.bodies[..]));
949            delim = ", ";
950        }
951        if !self.secrets.is_empty() {
952            f.write_str(delim);
953            f.write_node(&display::comma_separated(&self.secrets[..]));
954        }
955
956        f.write_str(")");
957    }
958}
959
960impl_display_t!(CreateWebhookSourceCheckOptions);
961
962/// `SECRET ... [AS ...] [BYTES]`
963#[derive(Debug, Clone, PartialEq, Eq, Hash)]
964pub struct CreateWebhookSourceSecret<T: AstInfo> {
965    pub secret: T::ItemName,
966    pub alias: Option<Ident>,
967    pub use_bytes: bool,
968}
969
970impl<T: AstInfo> AstDisplay for CreateWebhookSourceSecret<T> {
971    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
972        f.write_str("SECRET ");
973        f.write_node(&self.secret);
974
975        if let Some(alias) = &self.alias {
976            f.write_str(" AS ");
977            f.write_node(alias);
978        }
979
980        if self.use_bytes {
981            f.write_str(" BYTES");
982        }
983    }
984}
985
986impl_display_t!(CreateWebhookSourceSecret);
987
988/// `HEADER [AS ...] [BYTES]`
989#[derive(Debug, Clone, PartialEq, Eq, Hash)]
990pub struct CreateWebhookSourceHeader {
991    pub alias: Option<Ident>,
992    pub use_bytes: bool,
993}
994
995impl AstDisplay for CreateWebhookSourceHeader {
996    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
997        f.write_str("HEADERS");
998
999        if let Some(alias) = &self.alias {
1000            f.write_str(" AS ");
1001            f.write_node(alias);
1002        }
1003
1004        if self.use_bytes {
1005            f.write_str(" BYTES");
1006        }
1007    }
1008}
1009
1010impl_display!(CreateWebhookSourceHeader);
1011
1012/// `BODY [AS ...] [BYTES]`
1013#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1014pub struct CreateWebhookSourceBody {
1015    pub alias: Option<Ident>,
1016    pub use_bytes: bool,
1017}
1018
1019impl AstDisplay for CreateWebhookSourceBody {
1020    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1021        f.write_str("BODY");
1022
1023        if let Some(alias) = &self.alias {
1024            f.write_str(" AS ");
1025            f.write_node(alias);
1026        }
1027
1028        if self.use_bytes {
1029            f.write_str(" BYTES");
1030        }
1031    }
1032}
1033
1034impl_display!(CreateWebhookSourceBody);
1035
1036/// `INCLUDE [HEADER | HEADERS]`
1037#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
1038pub struct CreateWebhookSourceIncludeHeaders {
1039    /// Mapping individual header names to columns in the source.
1040    pub mappings: Vec<CreateWebhookSourceMapHeader>,
1041    /// Whether or not to include the `headers` column, and any filtering we might want to do.
1042    pub column: Option<Vec<CreateWebhookSourceFilterHeader>>,
1043}
1044
1045impl AstDisplay for CreateWebhookSourceIncludeHeaders {
1046    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1047        if !self.mappings.is_empty() {
1048            f.write_str(" ");
1049        }
1050        f.write_node(&display::separated(&self.mappings[..], " "));
1051
1052        if let Some(column) = &self.column {
1053            f.write_str(" INCLUDE HEADERS");
1054
1055            if !column.is_empty() {
1056                f.write_str(" ");
1057                f.write_str("(");
1058                f.write_node(&display::comma_separated(&column[..]));
1059                f.write_str(")");
1060            }
1061        }
1062    }
1063}
1064
1065impl_display!(CreateWebhookSourceIncludeHeaders);
1066
1067#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1068pub struct CreateWebhookSourceFilterHeader {
1069    pub block: bool,
1070    pub header_name: String,
1071}
1072
1073impl AstDisplay for CreateWebhookSourceFilterHeader {
1074    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1075        if self.block {
1076            f.write_str("NOT ");
1077        }
1078        f.write_node(&display::escaped_string_literal(&self.header_name));
1079    }
1080}
1081
1082impl_display!(CreateWebhookSourceFilterHeader);
1083
1084/// `INCLUDE HEADER <name> [AS <alias>] [BYTES]`
1085#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1086pub struct CreateWebhookSourceMapHeader {
1087    pub header_name: String,
1088    pub column_name: Ident,
1089    pub use_bytes: bool,
1090}
1091
1092impl AstDisplay for CreateWebhookSourceMapHeader {
1093    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1094        f.write_str("INCLUDE HEADER ");
1095
1096        f.write_node(&display::escaped_string_literal(&self.header_name));
1097
1098        f.write_str(" AS ");
1099        f.write_node(&self.column_name);
1100
1101        if self.use_bytes {
1102            f.write_str(" BYTES");
1103        }
1104    }
1105}
1106
1107impl_display!(CreateWebhookSourceMapHeader);
1108
1109/// `CREATE SOURCE`
1110#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1111pub struct CreateSourceStatement<T: AstInfo> {
1112    pub name: UnresolvedItemName,
1113    pub in_cluster: Option<T::ClusterName>,
1114    pub col_names: Vec<Ident>,
1115    pub connection: CreateSourceConnection<T>,
1116    pub include_metadata: Vec<SourceIncludeMetadata>,
1117    pub format: Option<FormatSpecifier<T>>,
1118    pub envelope: Option<SourceEnvelope>,
1119    pub if_not_exists: bool,
1120    pub key_constraint: Option<KeyConstraint>,
1121    pub with_options: Vec<CreateSourceOption<T>>,
1122    pub external_references: Option<ExternalReferences>,
1123    pub progress_subsource: Option<DeferredItemName<T>>,
1124}
1125
1126impl<T: AstInfo> AstDisplay for CreateSourceStatement<T> {
1127    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1128        f.write_str("CREATE SOURCE ");
1129        if self.if_not_exists {
1130            f.write_str("IF NOT EXISTS ");
1131        }
1132        f.write_node(&self.name);
1133        if !self.col_names.is_empty() {
1134            f.write_str(" (");
1135            f.write_node(&display::comma_separated(&self.col_names));
1136            if let Some(key_constraint) = &self.key_constraint {
1137                f.write_str(", ");
1138                f.write_node(key_constraint);
1139            }
1140            f.write_str(")");
1141        } else if let Some(key_constraint) = &self.key_constraint {
1142            f.write_str(" (");
1143            f.write_node(key_constraint);
1144            f.write_str(")")
1145        }
1146        if let Some(cluster) = &self.in_cluster {
1147            f.write_str(" IN CLUSTER ");
1148            f.write_node(cluster);
1149        }
1150        f.write_str(" FROM ");
1151        f.write_node(&self.connection);
1152        if let Some(format) = &self.format {
1153            f.write_str(" ");
1154            f.write_node(format);
1155        }
1156        if !self.include_metadata.is_empty() {
1157            f.write_str(" INCLUDE ");
1158            f.write_node(&display::comma_separated(&self.include_metadata));
1159        }
1160
1161        if let Some(envelope) = &self.envelope {
1162            f.write_str(" ENVELOPE ");
1163            f.write_node(envelope);
1164        }
1165
1166        if let Some(subsources) = &self.external_references {
1167            f.write_str(" ");
1168            f.write_node(subsources);
1169        }
1170
1171        if let Some(progress) = &self.progress_subsource {
1172            f.write_str(" EXPOSE PROGRESS AS ");
1173            f.write_node(progress);
1174        }
1175
1176        if !self.with_options.is_empty() {
1177            f.write_str(" WITH (");
1178            f.write_node(&display::comma_separated(&self.with_options));
1179            f.write_str(")");
1180        }
1181    }
1182}
1183impl_display_t!(CreateSourceStatement);
1184
1185/// A selected external reference in a FOR TABLES (..) statement
1186#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1187pub struct ExternalReferenceExport {
1188    pub reference: UnresolvedItemName,
1189    pub alias: Option<UnresolvedItemName>,
1190}
1191
1192impl AstDisplay for ExternalReferenceExport {
1193    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1194        f.write_node(&self.reference);
1195        if let Some(alias) = &self.alias {
1196            f.write_str(" AS ");
1197            f.write_node(alias);
1198        }
1199    }
1200}
1201impl_display!(ExternalReferenceExport);
1202
1203/// Specifies which set of external references to generate a source export
1204/// for in a `CREATE SOURCE` statement.
1205#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1206pub enum ExternalReferences {
1207    /// A subset defined with FOR TABLES (...)
1208    SubsetTables(Vec<ExternalReferenceExport>),
1209    /// A subset defined with FOR SCHEMAS (...)
1210    SubsetSchemas(Vec<Ident>),
1211    /// FOR ALL TABLES
1212    All,
1213}
1214
1215impl AstDisplay for ExternalReferences {
1216    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1217        match self {
1218            Self::SubsetTables(tables) => {
1219                f.write_str("FOR TABLES (");
1220                f.write_node(&display::comma_separated(tables));
1221                f.write_str(")");
1222            }
1223            Self::SubsetSchemas(schemas) => {
1224                f.write_str("FOR SCHEMAS (");
1225                f.write_node(&display::comma_separated(schemas));
1226                f.write_str(")");
1227            }
1228            Self::All => f.write_str("FOR ALL TABLES"),
1229        }
1230    }
1231}
1232impl_display!(ExternalReferences);
1233
1234/// An option in a `CREATE SUBSOURCE` statement.
1235#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1236pub enum CreateSubsourceOptionName {
1237    Progress,
1238    /// Tracks which item this subsource references in the primary source.
1239    ExternalReference,
1240    /// The `RETAIN HISTORY` option
1241    RetainHistory,
1242    /// Columns whose types you want to unconditionally format as text
1243    TextColumns,
1244    /// Columns you want to exclude when ingesting data
1245    ExcludeColumns,
1246    /// `DETAILS` for this subsource, hex-encoded protobuf type
1247    /// `mz_storage_types::sources::SourceExportStatementDetails`
1248    Details,
1249}
1250
1251impl AstDisplay for CreateSubsourceOptionName {
1252    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1253        f.write_str(match self {
1254            CreateSubsourceOptionName::Progress => "PROGRESS",
1255            CreateSubsourceOptionName::ExternalReference => "EXTERNAL REFERENCE",
1256            CreateSubsourceOptionName::RetainHistory => "RETAIN HISTORY",
1257            CreateSubsourceOptionName::TextColumns => "TEXT COLUMNS",
1258            CreateSubsourceOptionName::ExcludeColumns => "EXCLUDE COLUMNS",
1259            CreateSubsourceOptionName::Details => "DETAILS",
1260        })
1261    }
1262}
1263
1264impl WithOptionName for CreateSubsourceOptionName {
1265    /// # WARNING
1266    ///
1267    /// Whenever implementing this trait consider very carefully whether or not
1268    /// this value could contain sensitive user data. If you're uncertain, err
1269    /// on the conservative side and return `true`.
1270    fn redact_value(&self) -> bool {
1271        match self {
1272            CreateSubsourceOptionName::Progress
1273            | CreateSubsourceOptionName::ExternalReference
1274            | CreateSubsourceOptionName::RetainHistory
1275            | CreateSubsourceOptionName::Details
1276            | CreateSubsourceOptionName::TextColumns
1277            | CreateSubsourceOptionName::ExcludeColumns => false,
1278        }
1279    }
1280}
1281
1282#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1283pub struct CreateSubsourceOption<T: AstInfo> {
1284    pub name: CreateSubsourceOptionName,
1285    pub value: Option<WithOptionValue<T>>,
1286}
1287impl_display_for_with_option!(CreateSubsourceOption);
1288
1289/// `CREATE SUBSOURCE`
1290#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1291pub struct CreateSubsourceStatement<T: AstInfo> {
1292    pub name: UnresolvedItemName,
1293    pub columns: Vec<ColumnDef<T>>,
1294    /// Tracks the primary source of this subsource if an ingestion export (i.e.
1295    /// not a progress subsource).
1296    pub of_source: Option<T::ItemName>,
1297    pub constraints: Vec<TableConstraint<T>>,
1298    pub if_not_exists: bool,
1299    pub with_options: Vec<CreateSubsourceOption<T>>,
1300}
1301
1302impl<T: AstInfo> AstDisplay for CreateSubsourceStatement<T> {
1303    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1304        f.write_str("CREATE SUBSOURCE ");
1305        if self.if_not_exists {
1306            f.write_str("IF NOT EXISTS ");
1307        }
1308
1309        f.write_node(&self.name);
1310        f.write_str(" (");
1311        f.write_node(&display::comma_separated(&self.columns));
1312        if !self.constraints.is_empty() {
1313            f.write_str(", ");
1314            f.write_node(&display::comma_separated(&self.constraints));
1315        }
1316        f.write_str(")");
1317
1318        if let Some(of_source) = &self.of_source {
1319            f.write_str(" OF SOURCE ");
1320            f.write_node(of_source);
1321        }
1322
1323        if !self.with_options.is_empty() {
1324            f.write_str(" WITH (");
1325            f.write_node(&display::comma_separated(&self.with_options));
1326            f.write_str(")");
1327        }
1328    }
1329}
1330impl_display_t!(CreateSubsourceStatement);
1331
1332/// An option in a `CREATE SINK` statement.
1333#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1334pub enum CreateSinkOptionName {
1335    Snapshot,
1336    Version,
1337    PartitionStrategy,
1338    CommitInterval,
1339}
1340
1341impl AstDisplay for CreateSinkOptionName {
1342    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1343        match self {
1344            CreateSinkOptionName::Snapshot => {
1345                f.write_str("SNAPSHOT");
1346            }
1347            CreateSinkOptionName::Version => {
1348                f.write_str("VERSION");
1349            }
1350            CreateSinkOptionName::PartitionStrategy => {
1351                f.write_str("PARTITION STRATEGY");
1352            }
1353            CreateSinkOptionName::CommitInterval => {
1354                f.write_str("COMMIT INTERVAL");
1355            }
1356        }
1357    }
1358}
1359
1360impl WithOptionName for CreateSinkOptionName {
1361    /// # WARNING
1362    ///
1363    /// Whenever implementing this trait consider very carefully whether or not
1364    /// this value could contain sensitive user data. If you're uncertain, err
1365    /// on the conservative side and return `true`.
1366    fn redact_value(&self) -> bool {
1367        match self {
1368            CreateSinkOptionName::Snapshot => false,
1369            CreateSinkOptionName::Version => false,
1370            CreateSinkOptionName::PartitionStrategy => false,
1371            CreateSinkOptionName::CommitInterval => false,
1372        }
1373    }
1374}
1375
1376#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1377pub struct CreateSinkOption<T: AstInfo> {
1378    pub name: CreateSinkOptionName,
1379    pub value: Option<WithOptionValue<T>>,
1380}
1381impl_display_for_with_option!(CreateSinkOption);
1382
1383/// `CREATE SINK`
1384#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1385pub struct CreateSinkStatement<T: AstInfo> {
1386    pub name: Option<UnresolvedItemName>,
1387    pub in_cluster: Option<T::ClusterName>,
1388    pub if_not_exists: bool,
1389    pub from: T::ItemName,
1390    pub connection: CreateSinkConnection<T>,
1391    pub format: Option<FormatSpecifier<T>>,
1392    pub envelope: Option<SinkEnvelope>,
1393    pub mode: Option<IcebergSinkMode>,
1394    pub with_options: Vec<CreateSinkOption<T>>,
1395}
1396
1397impl<T: AstInfo> AstDisplay for CreateSinkStatement<T> {
1398    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1399        f.write_str("CREATE SINK ");
1400        if self.if_not_exists {
1401            f.write_str("IF NOT EXISTS ");
1402        }
1403        if let Some(name) = &self.name {
1404            f.write_node(&name);
1405            f.write_str(" ");
1406        }
1407        if let Some(cluster) = &self.in_cluster {
1408            f.write_str("IN CLUSTER ");
1409            f.write_node(cluster);
1410            f.write_str(" ");
1411        }
1412        f.write_str("FROM ");
1413        f.write_node(&self.from);
1414        f.write_str(" INTO ");
1415        f.write_node(&self.connection);
1416        if let Some(format) = &self.format {
1417            f.write_str(" ");
1418            f.write_node(format);
1419        }
1420        if let Some(envelope) = &self.envelope {
1421            f.write_str(" ENVELOPE ");
1422            f.write_node(envelope);
1423        }
1424        if let Some(mode) = &self.mode {
1425            f.write_str(" MODE ");
1426            f.write_node(mode);
1427        }
1428
1429        if !self.with_options.is_empty() {
1430            f.write_str(" WITH (");
1431            f.write_node(&display::comma_separated(&self.with_options));
1432            f.write_str(")");
1433        }
1434    }
1435}
1436impl_display_t!(CreateSinkStatement);
1437
1438/// An option in a `CREATE METRIC SINK` statement.
1439#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1440pub enum CreateMetricSinkOptionName {
1441    Prefix,
1442}
1443
1444impl AstDisplay for CreateMetricSinkOptionName {
1445    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1446        match self {
1447            CreateMetricSinkOptionName::Prefix => {
1448                f.write_str("PREFIX");
1449            }
1450        }
1451    }
1452}
1453
1454impl WithOptionName for CreateMetricSinkOptionName {
1455    /// # WARNING
1456    ///
1457    /// Whenever implementing this trait consider very carefully whether or not
1458    /// this value could contain sensitive user data. If you're uncertain, err
1459    /// on the conservative side and return `true`.
1460    fn redact_value(&self) -> bool {
1461        match self {
1462            CreateMetricSinkOptionName::Prefix => false,
1463        }
1464    }
1465}
1466
1467#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1468pub struct CreateMetricSinkOption<T: AstInfo> {
1469    pub name: CreateMetricSinkOptionName,
1470    pub value: Option<WithOptionValue<T>>,
1471}
1472impl_display_for_with_option!(CreateMetricSinkOption);
1473
1474/// `CREATE METRIC SINK`
1475#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1476pub struct CreateMetricSinkStatement<T: AstInfo> {
1477    pub name: UnresolvedItemName,
1478    pub in_cluster: Option<T::ClusterName>,
1479    pub if_not_exists: bool,
1480    pub from: T::ItemName,
1481    pub with_options: Vec<CreateMetricSinkOption<T>>,
1482}
1483
1484impl<T: AstInfo> AstDisplay for CreateMetricSinkStatement<T> {
1485    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1486        f.write_str("CREATE METRIC SINK ");
1487        if self.if_not_exists {
1488            f.write_str("IF NOT EXISTS ");
1489        }
1490        f.write_node(&self.name);
1491        f.write_str(" ");
1492        if let Some(cluster) = &self.in_cluster {
1493            f.write_str("IN CLUSTER ");
1494            f.write_node(cluster);
1495            f.write_str(" ");
1496        }
1497        f.write_str("FROM ");
1498        f.write_node(&self.from);
1499
1500        // NOTE: `create_sql` is persisted through this impl and re-parsed on boot, so dropping
1501        // the clause here would silently lose the prefix across a restart.
1502        if !self.with_options.is_empty() {
1503            f.write_str(" WITH (");
1504            f.write_node(&display::comma_separated(&self.with_options));
1505            f.write_str(")");
1506        }
1507    }
1508}
1509impl_display_t!(CreateMetricSinkStatement);
1510
1511#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1512pub struct ViewDefinition<T: AstInfo> {
1513    /// View name
1514    pub name: UnresolvedItemName,
1515    pub columns: Vec<Ident>,
1516    pub query: Query<T>,
1517}
1518
1519impl<T: AstInfo> AstDisplay for ViewDefinition<T> {
1520    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1521        f.write_node(&self.name);
1522
1523        if !self.columns.is_empty() {
1524            f.write_str(" (");
1525            f.write_node(&display::comma_separated(&self.columns));
1526            f.write_str(")");
1527        }
1528
1529        f.write_str(" AS ");
1530        f.write_node(&self.query);
1531    }
1532}
1533impl_display_t!(ViewDefinition);
1534
1535/// `CREATE VIEW`
1536#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1537pub struct CreateViewStatement<T: AstInfo> {
1538    pub if_exists: IfExistsBehavior,
1539    pub temporary: bool,
1540    pub definition: ViewDefinition<T>,
1541}
1542
1543impl<T: AstInfo> AstDisplay for CreateViewStatement<T> {
1544    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1545        f.write_str("CREATE");
1546        if self.if_exists == IfExistsBehavior::Replace {
1547            f.write_str(" OR REPLACE");
1548        }
1549        if self.temporary {
1550            f.write_str(" TEMPORARY");
1551        }
1552
1553        f.write_str(" VIEW");
1554
1555        if self.if_exists == IfExistsBehavior::Skip {
1556            f.write_str(" IF NOT EXISTS");
1557        }
1558
1559        f.write_str(" ");
1560        f.write_node(&self.definition);
1561    }
1562}
1563impl_display_t!(CreateViewStatement);
1564
1565/// `CREATE MATERIALIZED VIEW`
1566#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1567pub struct CreateMaterializedViewStatement<T: AstInfo> {
1568    pub if_exists: IfExistsBehavior,
1569    pub name: UnresolvedItemName,
1570    pub columns: Vec<Ident>,
1571    pub replacement_for: Option<T::ItemName>,
1572    pub in_cluster: Option<T::ClusterName>,
1573    pub in_cluster_replica: Option<Ident>,
1574    pub query: Query<T>,
1575    pub as_of: Option<u64>,
1576    pub with_options: Vec<MaterializedViewOption<T>>,
1577}
1578
1579impl<T: AstInfo> AstDisplay for CreateMaterializedViewStatement<T> {
1580    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1581        f.write_str("CREATE");
1582        if self.if_exists == IfExistsBehavior::Replace {
1583            f.write_str(" OR REPLACE");
1584        }
1585        if self.replacement_for.is_some() {
1586            f.write_str(" REPLACEMENT");
1587        }
1588
1589        f.write_str(" MATERIALIZED VIEW");
1590
1591        if self.if_exists == IfExistsBehavior::Skip {
1592            f.write_str(" IF NOT EXISTS");
1593        }
1594
1595        f.write_str(" ");
1596        f.write_node(&self.name);
1597
1598        if !self.columns.is_empty() {
1599            f.write_str(" (");
1600            f.write_node(&display::comma_separated(&self.columns));
1601            f.write_str(")");
1602        }
1603
1604        if let Some(target) = &self.replacement_for {
1605            f.write_str(" FOR ");
1606            f.write_node(target);
1607        }
1608
1609        match (&self.in_cluster, &self.in_cluster_replica) {
1610            (Some(cluster), Some(replica)) => {
1611                f.write_str(" IN CLUSTER ");
1612                f.write_node(cluster);
1613                f.write_str(" REPLICA ");
1614                f.write_node(replica);
1615            }
1616            (Some(cluster), None) => {
1617                f.write_str(" IN CLUSTER ");
1618                f.write_node(cluster);
1619            }
1620            (None, Some(replica)) => {
1621                f.write_str(" IN REPLICA ");
1622                f.write_node(replica);
1623            }
1624            (None, None) => {}
1625        }
1626
1627        if !self.with_options.is_empty() {
1628            f.write_str(" WITH (");
1629            f.write_node(&display::comma_separated(&self.with_options));
1630            f.write_str(")");
1631        }
1632
1633        f.write_str(" AS ");
1634        f.write_node(&self.query);
1635
1636        if let Some(time) = &self.as_of {
1637            f.write_str(" AS OF ");
1638            f.write_str(time);
1639        }
1640    }
1641}
1642impl_display_t!(CreateMaterializedViewStatement);
1643
1644/// `ALTER SET CLUSTER`
1645#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1646pub struct AlterSetClusterStatement<T: AstInfo> {
1647    pub if_exists: bool,
1648    pub name: UnresolvedItemName,
1649    pub object_type: ObjectType,
1650    pub set_cluster: T::ClusterName,
1651}
1652
1653impl<T: AstInfo> AstDisplay for AlterSetClusterStatement<T> {
1654    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1655        f.write_str("ALTER ");
1656        f.write_node(&self.object_type);
1657
1658        if self.if_exists {
1659            f.write_str(" IF EXISTS");
1660        }
1661
1662        f.write_str(" ");
1663        f.write_node(&self.name);
1664
1665        f.write_str(" SET CLUSTER ");
1666        f.write_node(&self.set_cluster);
1667    }
1668}
1669impl_display_t!(AlterSetClusterStatement);
1670
1671/// `CREATE TABLE`
1672#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1673pub struct CreateTableStatement<T: AstInfo> {
1674    /// Table name
1675    pub name: UnresolvedItemName,
1676    /// Optional schema
1677    pub columns: Vec<ColumnDef<T>>,
1678    pub constraints: Vec<TableConstraint<T>>,
1679    pub if_not_exists: bool,
1680    pub temporary: bool,
1681    pub with_options: Vec<TableOption<T>>,
1682}
1683
1684impl<T: AstInfo> AstDisplay for CreateTableStatement<T> {
1685    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1686        let Self {
1687            name,
1688            columns,
1689            constraints,
1690            if_not_exists,
1691            temporary,
1692            with_options,
1693        } = self;
1694        f.write_str("CREATE ");
1695        if *temporary {
1696            f.write_str("TEMPORARY ");
1697        }
1698        f.write_str("TABLE ");
1699        if *if_not_exists {
1700            f.write_str("IF NOT EXISTS ");
1701        }
1702        f.write_node(name);
1703        f.write_str(" (");
1704        f.write_node(&display::comma_separated(columns));
1705        if !self.constraints.is_empty() {
1706            if !columns.is_empty() {
1707                f.write_str(", ");
1708            }
1709            f.write_node(&display::comma_separated(constraints));
1710        }
1711        f.write_str(")");
1712        if !with_options.is_empty() {
1713            f.write_str(" WITH (");
1714            f.write_node(&display::comma_separated(&self.with_options));
1715            f.write_str(")");
1716        }
1717    }
1718}
1719impl_display_t!(CreateTableStatement);
1720
1721#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1722pub enum TableOptionName {
1723    // The `PARTITION BY` option
1724    PartitionBy,
1725    // The `RETAIN HISTORY` option
1726    RetainHistory,
1727    /// A special option to test that we do redact values.
1728    RedactedTest,
1729}
1730
1731impl AstDisplay for TableOptionName {
1732    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1733        match self {
1734            TableOptionName::PartitionBy => {
1735                f.write_str("PARTITION BY");
1736            }
1737            TableOptionName::RetainHistory => {
1738                f.write_str("RETAIN HISTORY");
1739            }
1740            TableOptionName::RedactedTest => {
1741                f.write_str("REDACTED");
1742            }
1743        }
1744    }
1745}
1746
1747impl WithOptionName for TableOptionName {
1748    /// # WARNING
1749    ///
1750    /// Whenever implementing this trait consider very carefully whether or not
1751    /// this value could contain sensitive user data. If you're uncertain, err
1752    /// on the conservative side and return `true`.
1753    fn redact_value(&self) -> bool {
1754        match self {
1755            // The value is an arbitrary user expression/literal that may embed
1756            // sensitive data, so redact it (mirrors `KafkaSinkConfigOptionName`).
1757            TableOptionName::PartitionBy => true,
1758            TableOptionName::RetainHistory => false,
1759            TableOptionName::RedactedTest => true,
1760        }
1761    }
1762}
1763
1764#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1765pub struct TableOption<T: AstInfo> {
1766    pub name: TableOptionName,
1767    pub value: Option<WithOptionValue<T>>,
1768}
1769impl_display_for_with_option!(TableOption);
1770
1771#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1772pub enum TableFromSourceOptionName {
1773    /// Columns whose types you want to unconditionally format as text
1774    TextColumns,
1775    /// Columns you want to exclude when ingesting data
1776    ExcludeColumns,
1777    /// Upstream `PRIMARY KEY`/`UNIQUE` constraints, by name, that Materialize
1778    /// should not record as keys, so that their later upstream drop is a
1779    /// non-event.
1780    ExcludeConstraints,
1781    /// Record no upstream constraints at all: no keys, and every column
1782    /// ingested as nullable.
1783    ExcludeAllConstraints,
1784    /// Ignore all `NOT NULL` constraints on columns
1785    ExcludeAllNotNullConstraints,
1786    /// Hex-encoded protobuf of a `ProtoSourceExportStatementDetails`
1787    /// message, which includes details necessary for planning this
1788    /// table as a Source Export
1789    Details,
1790    /// Partition the given table by the provided columns.
1791    PartitionBy,
1792    // The `RETAIN HISTORY` option
1793    RetainHistory,
1794}
1795
1796impl AstDisplay for TableFromSourceOptionName {
1797    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1798        f.write_str(match self {
1799            TableFromSourceOptionName::TextColumns => "TEXT COLUMNS",
1800            TableFromSourceOptionName::ExcludeColumns => "EXCLUDE COLUMNS",
1801            TableFromSourceOptionName::ExcludeConstraints => "EXCLUDE CONSTRAINTS",
1802            TableFromSourceOptionName::ExcludeAllConstraints => "EXCLUDE ALL CONSTRAINTS",
1803            TableFromSourceOptionName::ExcludeAllNotNullConstraints => {
1804                "EXCLUDE ALL NOT NULL CONSTRAINTS"
1805            }
1806            TableFromSourceOptionName::Details => "DETAILS",
1807            TableFromSourceOptionName::PartitionBy => "PARTITION BY",
1808            TableFromSourceOptionName::RetainHistory => "RETAIN HISTORY",
1809        })
1810    }
1811}
1812impl_display!(TableFromSourceOptionName);
1813
1814impl WithOptionName for TableFromSourceOptionName {
1815    /// # WARNING
1816    ///
1817    /// Whenever implementing this trait consider very carefully whether or not
1818    /// this value could contain sensitive user data. If you're uncertain, err
1819    /// on the conservative side and return `true`.
1820    fn redact_value(&self) -> bool {
1821        match self {
1822            TableFromSourceOptionName::Details
1823            | TableFromSourceOptionName::TextColumns
1824            | TableFromSourceOptionName::ExcludeColumns
1825            | TableFromSourceOptionName::ExcludeConstraints
1826            | TableFromSourceOptionName::ExcludeAllConstraints
1827            | TableFromSourceOptionName::ExcludeAllNotNullConstraints
1828            | TableFromSourceOptionName::RetainHistory => false,
1829            // The value is an arbitrary user expression/literal that may embed
1830            // sensitive data, so redact it (mirrors `KafkaSinkConfigOptionName`).
1831            TableFromSourceOptionName::PartitionBy => true,
1832        }
1833    }
1834}
1835
1836#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1837pub struct TableFromSourceOption<T: AstInfo> {
1838    pub name: TableFromSourceOptionName,
1839    pub value: Option<WithOptionValue<T>>,
1840}
1841impl_display_for_with_option!(TableFromSourceOption);
1842
1843/// `CREATE TABLE .. FROM SOURCE` columns specification
1844/// can have 3 states:
1845/// Before purification they can be `NotSpecified` or `Named`
1846/// by the user to specify the column names to use.
1847/// After purification they can be in any of the 3 states.
1848/// For some source types we define the columns during purification
1849/// and for others the columns are defined during planning based
1850/// on the encoding option of the source.
1851#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1852pub enum TableFromSourceColumns<T: AstInfo> {
1853    /// The user did not specify which columns to use.
1854    NotSpecified,
1855    /// The user requested the named columns. Only compatible
1856    /// with source types that allow user-specified column names.
1857    Named(Vec<Ident>),
1858    /// Columns defined during purification for some source types.
1859    Defined(Vec<ColumnDef<T>>),
1860}
1861
1862/// `CREATE TABLE .. FROM SOURCE`
1863#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1864pub struct CreateTableFromSourceStatement<T: AstInfo> {
1865    pub name: UnresolvedItemName,
1866    pub columns: TableFromSourceColumns<T>,
1867    pub constraints: Vec<TableConstraint<T>>,
1868    pub if_not_exists: bool,
1869    pub source: T::ItemName,
1870    pub external_reference: Option<UnresolvedItemName>,
1871    pub with_options: Vec<TableFromSourceOption<T>>,
1872    pub include_metadata: Vec<SourceIncludeMetadata>,
1873    pub format: Option<FormatSpecifier<T>>,
1874    pub envelope: Option<SourceEnvelope>,
1875}
1876
1877impl<T: AstInfo> AstDisplay for CreateTableFromSourceStatement<T> {
1878    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1879        let Self {
1880            name,
1881            columns,
1882            constraints,
1883            source,
1884            external_reference,
1885            if_not_exists,
1886            with_options,
1887            include_metadata,
1888            format,
1889            envelope,
1890        } = self;
1891        f.write_str("CREATE TABLE ");
1892        if *if_not_exists {
1893            f.write_str("IF NOT EXISTS ");
1894        }
1895        f.write_node(name);
1896        if !matches!(columns, TableFromSourceColumns::NotSpecified) || !constraints.is_empty() {
1897            f.write_str(" (");
1898
1899            match columns {
1900                TableFromSourceColumns::NotSpecified => {}
1901                TableFromSourceColumns::Named(columns) => {
1902                    f.write_node(&display::comma_separated(columns))
1903                }
1904                TableFromSourceColumns::Defined(columns) => {
1905                    f.write_node(&display::comma_separated(columns))
1906                }
1907            };
1908            if !constraints.is_empty() {
1909                if !matches!(columns, TableFromSourceColumns::NotSpecified) {
1910                    f.write_str(", ");
1911                }
1912                f.write_node(&display::comma_separated(constraints));
1913            }
1914            f.write_str(")");
1915        }
1916        f.write_str(" FROM SOURCE ");
1917        f.write_node(source);
1918        if let Some(external_reference) = external_reference {
1919            f.write_str(" (REFERENCE = ");
1920            f.write_node(external_reference);
1921            f.write_str(")");
1922        }
1923
1924        if let Some(format) = &format {
1925            f.write_str(" ");
1926            f.write_node(format);
1927        }
1928        if !include_metadata.is_empty() {
1929            f.write_str(" INCLUDE ");
1930            f.write_node(&display::comma_separated(include_metadata));
1931        }
1932        if let Some(envelope) = &envelope {
1933            f.write_str(" ENVELOPE ");
1934            f.write_node(envelope);
1935        }
1936        if !with_options.is_empty() {
1937            f.write_str(" WITH (");
1938            f.write_node(&display::comma_separated(with_options));
1939            f.write_str(")");
1940        }
1941    }
1942}
1943impl_display_t!(CreateTableFromSourceStatement);
1944
1945/// `CREATE INDEX`
1946#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1947pub struct CreateIndexStatement<T: AstInfo> {
1948    /// Optional index name.
1949    pub name: Option<Ident>,
1950    pub in_cluster: Option<T::ClusterName>,
1951    /// `ON` table or view name
1952    pub on_name: T::ItemName,
1953    /// Expressions that form part of the index key. If not included, the
1954    /// key_parts will be inferred from the named object.
1955    pub key_parts: Option<Vec<Expr<T>>>,
1956    pub with_options: Vec<IndexOption<T>>,
1957    pub if_not_exists: bool,
1958}
1959
1960impl<T: AstInfo> AstDisplay for CreateIndexStatement<T> {
1961    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1962        f.write_str("CREATE ");
1963        if self.key_parts.is_none() {
1964            f.write_str("DEFAULT ");
1965        }
1966        f.write_str("INDEX ");
1967        if self.if_not_exists {
1968            f.write_str("IF NOT EXISTS ");
1969        }
1970        if let Some(name) = &self.name {
1971            // A bare `in` index name re-lexes as the start of the optional
1972            // `IN CLUSTER` clause below (`CREATE INDEX in ON …` fails to reparse
1973            // with "Expected ON, found IN"), so force it quoted. `in` is
1974            // legitimately bare in other name positions — e.g. a required
1975            // `CREATE SINK` name — so this is local to the optional-name +
1976            // `IN CLUSTER` ambiguity, not a `can_be_printed_bare` case.
1977            if name.as_str().eq_ignore_ascii_case("in") {
1978                f.write_str("\"");
1979                f.write_str(name.as_str());
1980                f.write_str("\"");
1981            } else {
1982                f.write_node(name);
1983            }
1984            f.write_str(" ");
1985        }
1986        if let Some(cluster) = &self.in_cluster {
1987            f.write_str("IN CLUSTER ");
1988            f.write_node(cluster);
1989            f.write_str(" ");
1990        }
1991        f.write_str("ON ");
1992        f.write_node(&self.on_name);
1993        if let Some(key_parts) = &self.key_parts {
1994            f.write_str(" (");
1995            f.write_node(&display::comma_separated(key_parts));
1996            f.write_str(")");
1997        }
1998        if !self.with_options.is_empty() {
1999            f.write_str(" WITH (");
2000            f.write_node(&display::comma_separated(&self.with_options));
2001            f.write_str(")");
2002        }
2003    }
2004}
2005impl_display_t!(CreateIndexStatement);
2006
2007/// An option in a `CREATE CLUSTER` statement.
2008#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2009pub enum IndexOptionName {
2010    // The `RETAIN HISTORY` option
2011    RetainHistory,
2012}
2013
2014impl AstDisplay for IndexOptionName {
2015    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2016        match self {
2017            IndexOptionName::RetainHistory => {
2018                f.write_str("RETAIN HISTORY");
2019            }
2020        }
2021    }
2022}
2023
2024impl WithOptionName for IndexOptionName {
2025    /// # WARNING
2026    ///
2027    /// Whenever implementing this trait consider very carefully whether or not
2028    /// this value could contain sensitive user data. If you're uncertain, err
2029    /// on the conservative side and return `true`.
2030    fn redact_value(&self) -> bool {
2031        match self {
2032            IndexOptionName::RetainHistory => false,
2033        }
2034    }
2035}
2036
2037#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2038pub struct IndexOption<T: AstInfo> {
2039    pub name: IndexOptionName,
2040    pub value: Option<WithOptionValue<T>>,
2041}
2042impl_display_for_with_option!(IndexOption);
2043
2044/// A `CREATE ROLE` statement.
2045#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2046pub struct CreateRoleStatement {
2047    /// The specified role.
2048    pub name: Ident,
2049    /// Any options that were attached, in the order they were presented.
2050    pub options: Vec<RoleAttribute>,
2051}
2052
2053impl AstDisplay for CreateRoleStatement {
2054    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2055        f.write_str("CREATE ");
2056        f.write_str("ROLE ");
2057        f.write_node(&self.name);
2058        for option in &self.options {
2059            f.write_str(" ");
2060            option.fmt(f)
2061        }
2062    }
2063}
2064impl_display!(CreateRoleStatement);
2065
2066/// Attributes that can be attached to roles.
2067#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2068pub enum RoleAttribute {
2069    /// The `INHERIT` option.
2070    Inherit,
2071    /// The `NOINHERIT` option.
2072    NoInherit,
2073    /// The `PASSWORD` option.
2074    Password(Option<String>),
2075    // The following are not supported, but included to give helpful error messages.
2076    Login,
2077    NoLogin,
2078    SuperUser,
2079    NoSuperUser,
2080    CreateCluster,
2081    NoCreateCluster,
2082    CreateDB,
2083    NoCreateDB,
2084    CreateRole,
2085    NoCreateRole,
2086}
2087
2088impl AstDisplay for RoleAttribute {
2089    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2090        match self {
2091            RoleAttribute::SuperUser => f.write_str("SUPERUSER"),
2092            RoleAttribute::NoSuperUser => f.write_str("NOSUPERUSER"),
2093            RoleAttribute::Login => f.write_str("LOGIN"),
2094            RoleAttribute::NoLogin => f.write_str("NOLOGIN"),
2095            RoleAttribute::Inherit => f.write_str("INHERIT"),
2096            RoleAttribute::NoInherit => f.write_str("NOINHERIT"),
2097            RoleAttribute::CreateCluster => f.write_str("CREATECLUSTER"),
2098            RoleAttribute::NoCreateCluster => f.write_str("NOCREATECLUSTER"),
2099            RoleAttribute::CreateDB => f.write_str("CREATEDB"),
2100            RoleAttribute::NoCreateDB => f.write_str("NOCREATEDB"),
2101            RoleAttribute::CreateRole => f.write_str("CREATEROLE"),
2102            RoleAttribute::NoCreateRole => f.write_str("NOCREATEROLE"),
2103            // `PASSWORD NULL` removes the password and carries no secret, so
2104            // print it verbatim. A `PASSWORD '<secret>'` is always redacted (in
2105            // every mode, matching the prior behavior) — but as a *parseable*
2106            // placeholder string, not a bare `PASSWORD`, which fails to reparse
2107            // (the grammar requires `NULL` or a string literal after `PASSWORD`).
2108            RoleAttribute::Password(None) => f.write_str("PASSWORD NULL"),
2109            RoleAttribute::Password(Some(_)) => f.write_str("PASSWORD '<REDACTED>'"),
2110        }
2111    }
2112}
2113impl_display!(RoleAttribute);
2114
2115/// `ALTER ROLE role_name [SET | RESET] ...`
2116#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2117pub enum SetRoleVar {
2118    /// `SET name TO value`
2119    Set { name: Ident, value: SetVariableTo },
2120    /// `RESET name`
2121    Reset { name: Ident },
2122}
2123
2124impl AstDisplay for SetRoleVar {
2125    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2126        match self {
2127            SetRoleVar::Set { name, value } => {
2128                f.write_str("SET ");
2129                f.write_node(name);
2130                f.write_str(" = ");
2131                f.write_node(value);
2132            }
2133            SetRoleVar::Reset { name } => {
2134                f.write_str("RESET ");
2135                f.write_node(name);
2136            }
2137        }
2138    }
2139}
2140impl_display!(SetRoleVar);
2141
2142/// AN `ALTER NETWORK POLICY` statement.
2143#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2144pub struct AlterNetworkPolicyStatement<T: AstInfo> {
2145    /// The specified Network Policy.
2146    pub name: Ident,
2147    /// Any options that were attached, in the order they were presented.
2148    pub options: Vec<NetworkPolicyOption<T>>,
2149}
2150
2151impl<T: AstInfo> AstDisplay for AlterNetworkPolicyStatement<T> {
2152    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2153        f.write_str("ALTER ");
2154        f.write_str("NETWORK POLICY ");
2155        f.write_node(&self.name);
2156        f.write_str(" SET (");
2157        f.write_node(&display::comma_separated(&self.options));
2158        f.write_str(" )");
2159    }
2160}
2161impl_display_t!(AlterNetworkPolicyStatement);
2162
2163/// A `CREATE NETWORK POLICY` statement.
2164#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2165pub struct CreateNetworkPolicyStatement<T: AstInfo> {
2166    /// The specified network policy.
2167    pub name: Ident,
2168    /// Any options that were attached, in the order they were presented.
2169    pub options: Vec<NetworkPolicyOption<T>>,
2170}
2171
2172impl<T: AstInfo> AstDisplay for CreateNetworkPolicyStatement<T> {
2173    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2174        f.write_str("CREATE ");
2175        f.write_str("NETWORK POLICY ");
2176        f.write_node(&self.name);
2177        f.write_str(" (");
2178        f.write_node(&display::comma_separated(&self.options));
2179        f.write_str(" )");
2180    }
2181}
2182impl_display_t!(CreateNetworkPolicyStatement);
2183
2184#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2185pub struct NetworkPolicyOption<T: AstInfo> {
2186    pub name: NetworkPolicyOptionName,
2187    pub value: Option<WithOptionValue<T>>,
2188}
2189
2190#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2191pub enum NetworkPolicyOptionName {
2192    Rules,
2193}
2194
2195impl WithOptionName for NetworkPolicyOptionName {
2196    /// # WARNING
2197    ///
2198    /// Whenever implementing this trait consider very carefully whether or not
2199    /// this value could contain sensitive user data. If you're uncertain, err
2200    /// on the conservative side and return `true`.
2201    fn redact_value(&self) -> bool {
2202        match self {
2203            NetworkPolicyOptionName::Rules => false,
2204        }
2205    }
2206}
2207
2208impl AstDisplay for NetworkPolicyOptionName {
2209    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2210        match self {
2211            NetworkPolicyOptionName::Rules => f.write_str("RULES"),
2212        }
2213    }
2214}
2215impl_display_for_with_option!(NetworkPolicyOption);
2216
2217#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2218pub struct NetworkPolicyRuleDefinition<T: AstInfo> {
2219    pub name: Ident,
2220    pub options: Vec<NetworkPolicyRuleOption<T>>,
2221}
2222
2223impl<T: AstInfo> AstDisplay for NetworkPolicyRuleDefinition<T> {
2224    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2225        f.write_node(&self.name);
2226        f.write_str(" (");
2227        f.write_node(&display::comma_separated(&self.options));
2228        f.write_str(" )");
2229    }
2230}
2231
2232#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2233pub struct NetworkPolicyRuleOption<T: AstInfo> {
2234    pub name: NetworkPolicyRuleOptionName,
2235    pub value: Option<WithOptionValue<T>>,
2236}
2237
2238#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2239pub enum NetworkPolicyRuleOptionName {
2240    Direction,
2241    Action,
2242    Address,
2243}
2244
2245impl WithOptionName for NetworkPolicyRuleOptionName {
2246    /// # WARNING
2247    ///
2248    /// Whenever implementing this trait consider very carefully whether or not
2249    /// this value could contain sensitive user data. If you're uncertain, err
2250    /// on the conservative side and return `true`.
2251    fn redact_value(&self) -> bool {
2252        match self {
2253            NetworkPolicyRuleOptionName::Direction
2254            | NetworkPolicyRuleOptionName::Action
2255            | NetworkPolicyRuleOptionName::Address => false,
2256        }
2257    }
2258}
2259
2260impl AstDisplay for NetworkPolicyRuleOptionName {
2261    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2262        match self {
2263            NetworkPolicyRuleOptionName::Direction => f.write_str("DIRECTION"),
2264            NetworkPolicyRuleOptionName::Action => f.write_str("ACTION"),
2265            NetworkPolicyRuleOptionName::Address => f.write_str("ADDRESS"),
2266        }
2267    }
2268}
2269
2270impl_display_for_with_option!(NetworkPolicyRuleOption);
2271
2272/// A `CREATE SECRET` statement.
2273#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2274pub struct CreateSecretStatement<T: AstInfo> {
2275    pub name: UnresolvedItemName,
2276    pub if_not_exists: bool,
2277    pub value: Expr<T>,
2278}
2279
2280impl<T: AstInfo> AstDisplay for CreateSecretStatement<T> {
2281    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2282        f.write_str("CREATE SECRET ");
2283        if self.if_not_exists {
2284            f.write_str("IF NOT EXISTS ");
2285        }
2286        f.write_node(&self.name);
2287        f.write_str(" AS ");
2288
2289        if f.redacted() {
2290            f.write_str("'<REDACTED>'");
2291        } else {
2292            f.write_node(&self.value);
2293        }
2294    }
2295}
2296impl_display_t!(CreateSecretStatement);
2297
2298/// `CREATE TYPE ..`
2299#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2300pub struct CreateTypeStatement<T: AstInfo> {
2301    /// Name of the created type.
2302    pub name: UnresolvedItemName,
2303    /// The new type's "base type".
2304    pub as_type: CreateTypeAs<T>,
2305}
2306
2307impl<T: AstInfo> AstDisplay for CreateTypeStatement<T> {
2308    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2309        f.write_str("CREATE TYPE ");
2310        f.write_node(&self.name);
2311        f.write_str(" AS ");
2312        match &self.as_type {
2313            CreateTypeAs::List { options } => {
2314                f.write_str(&self.as_type);
2315                f.write_str("(");
2316                if !options.is_empty() {
2317                    f.write_node(&display::comma_separated(options));
2318                }
2319                f.write_str(")");
2320            }
2321            CreateTypeAs::Map { options } => {
2322                f.write_str(&self.as_type);
2323                f.write_str("(");
2324                if !options.is_empty() {
2325                    f.write_node(&display::comma_separated(options));
2326                }
2327                f.write_str(")");
2328            }
2329            CreateTypeAs::Record { column_defs } => {
2330                f.write_str("(");
2331                if !column_defs.is_empty() {
2332                    f.write_node(&display::comma_separated(column_defs));
2333                }
2334                f.write_str(")");
2335            }
2336        };
2337    }
2338}
2339impl_display_t!(CreateTypeStatement);
2340
2341#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2342pub enum ClusterOptionName {
2343    /// The `AUTO SCALING STRATEGY [[=] (...)]` option.
2344    AutoScalingStrategy,
2345    /// The `AVAILABILITY ZONES [[=] '[' <values> ']' ]` option.
2346    AvailabilityZones,
2347    /// The `DISK` option.
2348    Disk,
2349    /// The `EXPERIMENTAL ARRANGEMENT COMPRESSION [[=] <enabled>]` option.
2350    ExperimentalArrangementCompression,
2351    /// The `INTROSPECTION INTERVAL [[=] <interval>]` option.
2352    IntrospectionInterval,
2353    /// The `INTROSPECTION DEBUGGING [[=] <enabled>]` option.
2354    IntrospectionDebugging,
2355    /// The `MANAGED` option.
2356    Managed,
2357    /// The `REPLICAS` option.
2358    Replicas,
2359    /// The `REPLICATION FACTOR` option.
2360    ReplicationFactor,
2361    /// The `SIZE` option.
2362    Size,
2363    /// The `SCHEDULE` option.
2364    Schedule,
2365    /// The `WORKLOAD CLASS` option.
2366    WorkloadClass,
2367}
2368
2369impl AstDisplay for ClusterOptionName {
2370    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2371        match self {
2372            ClusterOptionName::AutoScalingStrategy => f.write_str("AUTO SCALING STRATEGY"),
2373            ClusterOptionName::AvailabilityZones => f.write_str("AVAILABILITY ZONES"),
2374            ClusterOptionName::Disk => f.write_str("DISK"),
2375            ClusterOptionName::ExperimentalArrangementCompression => {
2376                f.write_str("EXPERIMENTAL ARRANGEMENT COMPRESSION")
2377            }
2378            ClusterOptionName::IntrospectionDebugging => f.write_str("INTROSPECTION DEBUGGING"),
2379            ClusterOptionName::IntrospectionInterval => f.write_str("INTROSPECTION INTERVAL"),
2380            ClusterOptionName::Managed => f.write_str("MANAGED"),
2381            ClusterOptionName::Replicas => f.write_str("REPLICAS"),
2382            ClusterOptionName::ReplicationFactor => f.write_str("REPLICATION FACTOR"),
2383            ClusterOptionName::Size => f.write_str("SIZE"),
2384            ClusterOptionName::Schedule => f.write_str("SCHEDULE"),
2385            ClusterOptionName::WorkloadClass => f.write_str("WORKLOAD CLASS"),
2386        }
2387    }
2388}
2389
2390impl WithOptionName for ClusterOptionName {
2391    /// # WARNING
2392    ///
2393    /// Whenever implementing this trait consider very carefully whether or not
2394    /// this value could contain sensitive user data. If you're uncertain, err
2395    /// on the conservative side and return `true`.
2396    fn redact_value(&self) -> bool {
2397        match self {
2398            ClusterOptionName::AutoScalingStrategy
2399            | ClusterOptionName::AvailabilityZones
2400            | ClusterOptionName::Disk
2401            | ClusterOptionName::ExperimentalArrangementCompression
2402            | ClusterOptionName::IntrospectionDebugging
2403            | ClusterOptionName::IntrospectionInterval
2404            | ClusterOptionName::Managed
2405            | ClusterOptionName::Replicas
2406            | ClusterOptionName::ReplicationFactor
2407            | ClusterOptionName::Size
2408            | ClusterOptionName::Schedule
2409            | ClusterOptionName::WorkloadClass => false,
2410        }
2411    }
2412}
2413
2414#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2415/// An option in a `CREATE CLUSTER` statement.
2416pub struct ClusterOption<T: AstInfo> {
2417    pub name: ClusterOptionName,
2418    pub value: Option<WithOptionValue<T>>,
2419}
2420impl_display_for_with_option!(ClusterOption);
2421
2422#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2423pub enum ClusterAlterUntilReadyOptionName {
2424    Timeout,
2425    OnTimeout,
2426}
2427
2428impl AstDisplay for ClusterAlterUntilReadyOptionName {
2429    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2430        match self {
2431            Self::Timeout => f.write_str("TIMEOUT"),
2432            Self::OnTimeout => f.write_str("ON TIMEOUT"),
2433        }
2434    }
2435}
2436
2437impl WithOptionName for ClusterAlterUntilReadyOptionName {
2438    /// # WARNING
2439    ///
2440    /// Whenever implementing this trait consider very carefully whether or not
2441    /// this value could contain sensitive user data. If you're uncertain, err
2442    /// on the conservative side and return `true`.
2443    fn redact_value(&self) -> bool {
2444        match self {
2445            ClusterAlterUntilReadyOptionName::Timeout
2446            | ClusterAlterUntilReadyOptionName::OnTimeout => false,
2447        }
2448    }
2449}
2450
2451#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2452pub struct ClusterAlterUntilReadyOption<T: AstInfo> {
2453    pub name: ClusterAlterUntilReadyOptionName,
2454    pub value: Option<WithOptionValue<T>>,
2455}
2456impl_display_for_with_option!(ClusterAlterUntilReadyOption);
2457
2458#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2459pub enum ClusterAlterOptionName {
2460    Wait,
2461}
2462
2463impl AstDisplay for ClusterAlterOptionName {
2464    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2465        match self {
2466            ClusterAlterOptionName::Wait => f.write_str("WAIT"),
2467        }
2468    }
2469}
2470
2471impl WithOptionName for ClusterAlterOptionName {
2472    /// # WARNING
2473    ///
2474    /// Whenever implementing this trait consider very carefully whether or not
2475    /// this value could contain sensitive user data. If you're uncertain, err
2476    /// on the conservative side and return `true`.
2477    fn redact_value(&self) -> bool {
2478        match self {
2479            ClusterAlterOptionName::Wait => false,
2480        }
2481    }
2482}
2483
2484#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2485pub enum ClusterAlterOptionValue<T: AstInfo> {
2486    For(Value),
2487    UntilReady(Vec<ClusterAlterUntilReadyOption<T>>),
2488}
2489
2490impl<T: AstInfo> AstDisplay for ClusterAlterOptionValue<T> {
2491    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2492        match self {
2493            ClusterAlterOptionValue::For(duration) => {
2494                f.write_str("FOR ");
2495                f.write_node(duration);
2496            }
2497            ClusterAlterOptionValue::UntilReady(options) => {
2498                f.write_str("UNTIL READY (");
2499                f.write_node(&display::comma_separated(options));
2500                f.write_str(")");
2501            }
2502        }
2503    }
2504}
2505
2506#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2507/// An option in a `ALTER CLUSTER... WITH` statement.
2508pub struct ClusterAlterOption<T: AstInfo> {
2509    pub name: ClusterAlterOptionName,
2510    pub value: Option<WithOptionValue<T>>,
2511}
2512
2513impl_display_for_with_option!(ClusterAlterOption);
2514
2515// Note: the `AstDisplay` implementation and `Parser::parse_` method for this
2516// enum are generated automatically by this crate's `build.rs`.
2517#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2518pub enum ClusterFeatureName {
2519    ReoptimizeImportedViews,
2520    EnableNewOuterJoinLowering,
2521    EnableEagerDeltaJoins,
2522    EnableVariadicLeftJoinLowering,
2523    EnableLetrecFixpointAnalysis,
2524    EnableJoinPrioritizeArranged,
2525    EnableProjectionPushdownAfterRelationCse,
2526    EnableUnionCancellationAfterRelationCse,
2527}
2528
2529impl WithOptionName for ClusterFeatureName {
2530    /// # WARNING
2531    ///
2532    /// Whenever implementing this trait consider very carefully whether or not
2533    /// this value could contain sensitive user data. If you're uncertain, err
2534    /// on the conservative side and return `true`.
2535    fn redact_value(&self) -> bool {
2536        match self {
2537            Self::ReoptimizeImportedViews
2538            | Self::EnableNewOuterJoinLowering
2539            | Self::EnableEagerDeltaJoins
2540            | Self::EnableVariadicLeftJoinLowering
2541            | Self::EnableLetrecFixpointAnalysis
2542            | Self::EnableJoinPrioritizeArranged
2543            | Self::EnableProjectionPushdownAfterRelationCse
2544            | Self::EnableUnionCancellationAfterRelationCse => false,
2545        }
2546    }
2547}
2548
2549#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2550pub struct ClusterFeature<T: AstInfo> {
2551    pub name: ClusterFeatureName,
2552    pub value: Option<WithOptionValue<T>>,
2553}
2554impl_display_for_with_option!(ClusterFeature);
2555
2556/// `CREATE CLUSTER ..`
2557#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2558pub struct CreateClusterStatement<T: AstInfo> {
2559    /// Name of the created cluster.
2560    pub name: Ident,
2561    /// The comma-separated options.
2562    pub options: Vec<ClusterOption<T>>,
2563    /// The comma-separated features enabled on the cluster.
2564    pub features: Vec<ClusterFeature<T>>,
2565    pub if_not_exists: bool,
2566}
2567
2568impl<T: AstInfo> AstDisplay for CreateClusterStatement<T> {
2569    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2570        f.write_str("CREATE CLUSTER ");
2571        if self.if_not_exists {
2572            f.write_str("IF NOT EXISTS ");
2573        }
2574        f.write_node(&self.name);
2575        if !self.options.is_empty() {
2576            f.write_str(" (");
2577            f.write_node(&display::comma_separated(&self.options));
2578            f.write_str(")");
2579        }
2580        if !self.features.is_empty() {
2581            f.write_str(" FEATURES (");
2582            f.write_node(&display::comma_separated(&self.features));
2583            f.write_str(")");
2584        }
2585    }
2586}
2587impl_display_t!(CreateClusterStatement);
2588
2589#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2590pub struct ReplicaDefinition<T: AstInfo> {
2591    /// Name of the created replica.
2592    pub name: Ident,
2593    /// The comma-separated options.
2594    pub options: Vec<ReplicaOption<T>>,
2595}
2596
2597// Note that this display is meant for replicas defined inline when creating
2598// clusters.
2599impl<T: AstInfo> AstDisplay for ReplicaDefinition<T> {
2600    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2601        f.write_node(&self.name);
2602        f.write_str(" (");
2603        f.write_node(&display::comma_separated(&self.options));
2604        f.write_str(")");
2605    }
2606}
2607impl_display_t!(ReplicaDefinition);
2608
2609#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2610pub enum AlterClusterAction<T: AstInfo> {
2611    SetOptions {
2612        options: Vec<ClusterOption<T>>,
2613        with_options: Vec<ClusterAlterOption<T>>,
2614    },
2615    ResetOptions(Vec<ClusterOptionName>),
2616}
2617
2618/// `ALTER CLUSTER .. SET ...`
2619#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2620pub struct AlterClusterStatement<T: AstInfo> {
2621    /// The `IF EXISTS` option.
2622    pub if_exists: bool,
2623    /// Name of the altered cluster.
2624    pub name: Ident,
2625    /// The action.
2626    pub action: AlterClusterAction<T>,
2627}
2628
2629impl<T: AstInfo> AstDisplay for AlterClusterStatement<T> {
2630    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2631        f.write_str("ALTER CLUSTER ");
2632        if self.if_exists {
2633            f.write_str("IF EXISTS ");
2634        }
2635        f.write_node(&self.name);
2636        f.write_str(" ");
2637        match &self.action {
2638            AlterClusterAction::SetOptions {
2639                options,
2640                with_options,
2641            } => {
2642                f.write_str("SET (");
2643                f.write_node(&display::comma_separated(options));
2644                f.write_str(")");
2645                if !with_options.is_empty() {
2646                    f.write_str(" WITH (");
2647                    f.write_node(&display::comma_separated(with_options));
2648                    f.write_str(")");
2649                }
2650            }
2651            AlterClusterAction::ResetOptions(options) => {
2652                f.write_str("RESET (");
2653                f.write_node(&display::comma_separated(options));
2654                f.write_str(")");
2655            }
2656        }
2657    }
2658}
2659impl_display_t!(AlterClusterStatement);
2660
2661/// `CREATE CLUSTER REPLICA ..`
2662#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2663pub struct CreateClusterReplicaStatement<T: AstInfo> {
2664    /// Name of the replica's cluster.
2665    pub of_cluster: Ident,
2666    /// The replica's definition.
2667    pub definition: ReplicaDefinition<T>,
2668    pub if_not_exists: bool,
2669}
2670
2671impl<T: AstInfo> AstDisplay for CreateClusterReplicaStatement<T> {
2672    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2673        f.write_str("CREATE CLUSTER REPLICA ");
2674        if self.if_not_exists {
2675            f.write_str("IF NOT EXISTS ");
2676        }
2677        f.write_node(&self.of_cluster);
2678        f.write_str(".");
2679        f.write_node(&self.definition.name);
2680        f.write_str(" (");
2681        f.write_node(&display::comma_separated(&self.definition.options));
2682        f.write_str(")");
2683    }
2684}
2685impl_display_t!(CreateClusterReplicaStatement);
2686
2687#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2688pub enum ReplicaOptionName {
2689    /// The `BILLED AS [=] <value>` option.
2690    BilledAs,
2691    /// The `SIZE [[=] <size>]` option.
2692    Size,
2693    /// The `AVAILABILITY ZONE [[=] <id>]` option.
2694    AvailabilityZone,
2695    /// The `STORAGE ADDRESSES` option.
2696    StorageAddresses,
2697    /// The `STORAGECTL ADDRESSES` option.
2698    StoragectlAddresses,
2699    /// The `COMPUTECTL ADDRESSES` option.
2700    ComputectlAddresses,
2701    /// The `COMPUTE ADDRESSES` option.
2702    ComputeAddresses,
2703    /// The `WORKERS` option.
2704    Workers,
2705    /// The `INTERNAL` option.
2706    Internal,
2707    /// The `INTROSPECTION INTERVAL [[=] <interval>]` option.
2708    IntrospectionInterval,
2709    /// The `INTROSPECTION DEBUGGING [[=] <enabled>]` option.
2710    IntrospectionDebugging,
2711    /// The `DISK [[=] <enabled>]` option.
2712    Disk,
2713    /// The `EXPERIMENTAL ARRANGEMENT COMPRESSION [[=] <enabled>]` option.
2714    ExperimentalArrangementCompression,
2715}
2716
2717impl AstDisplay for ReplicaOptionName {
2718    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2719        match self {
2720            ReplicaOptionName::BilledAs => f.write_str("BILLED AS"),
2721            ReplicaOptionName::Size => f.write_str("SIZE"),
2722            ReplicaOptionName::AvailabilityZone => f.write_str("AVAILABILITY ZONE"),
2723            ReplicaOptionName::StorageAddresses => f.write_str("STORAGE ADDRESSES"),
2724            ReplicaOptionName::StoragectlAddresses => f.write_str("STORAGECTL ADDRESSES"),
2725            ReplicaOptionName::ComputectlAddresses => f.write_str("COMPUTECTL ADDRESSES"),
2726            ReplicaOptionName::ComputeAddresses => f.write_str("COMPUTE ADDRESSES"),
2727            ReplicaOptionName::Workers => f.write_str("WORKERS"),
2728            ReplicaOptionName::Internal => f.write_str("INTERNAL"),
2729            ReplicaOptionName::IntrospectionInterval => f.write_str("INTROSPECTION INTERVAL"),
2730            ReplicaOptionName::IntrospectionDebugging => f.write_str("INTROSPECTION DEBUGGING"),
2731            ReplicaOptionName::Disk => f.write_str("DISK"),
2732            ReplicaOptionName::ExperimentalArrangementCompression => {
2733                f.write_str("EXPERIMENTAL ARRANGEMENT COMPRESSION")
2734            }
2735        }
2736    }
2737}
2738
2739impl WithOptionName for ReplicaOptionName {
2740    /// # WARNING
2741    ///
2742    /// Whenever implementing this trait consider very carefully whether or not
2743    /// this value could contain sensitive user data. If you're uncertain, err
2744    /// on the conservative side and return `true`.
2745    fn redact_value(&self) -> bool {
2746        match self {
2747            ReplicaOptionName::BilledAs
2748            | ReplicaOptionName::Size
2749            | ReplicaOptionName::AvailabilityZone
2750            | ReplicaOptionName::StorageAddresses
2751            | ReplicaOptionName::StoragectlAddresses
2752            | ReplicaOptionName::ComputectlAddresses
2753            | ReplicaOptionName::ComputeAddresses
2754            | ReplicaOptionName::Workers
2755            | ReplicaOptionName::Internal
2756            | ReplicaOptionName::IntrospectionInterval
2757            | ReplicaOptionName::IntrospectionDebugging
2758            | ReplicaOptionName::Disk
2759            | ReplicaOptionName::ExperimentalArrangementCompression => false,
2760        }
2761    }
2762}
2763
2764#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2765/// An option in a `CREATE CLUSTER REPLICA` statement.
2766pub struct ReplicaOption<T: AstInfo> {
2767    pub name: ReplicaOptionName,
2768    pub value: Option<WithOptionValue<T>>,
2769}
2770impl_display_for_with_option!(ReplicaOption);
2771
2772/// `CREATE TYPE .. AS <TYPE>`
2773#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2774pub enum CreateTypeAs<T: AstInfo> {
2775    List {
2776        options: Vec<CreateTypeListOption<T>>,
2777    },
2778    Map {
2779        options: Vec<CreateTypeMapOption<T>>,
2780    },
2781    Record {
2782        column_defs: Vec<ColumnDef<T>>,
2783    },
2784}
2785
2786impl<T: AstInfo> AstDisplay for CreateTypeAs<T> {
2787    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2788        match self {
2789            CreateTypeAs::List { .. } => f.write_str("LIST "),
2790            CreateTypeAs::Map { .. } => f.write_str("MAP "),
2791            CreateTypeAs::Record { .. } => f.write_str("RECORD "),
2792        }
2793    }
2794}
2795impl_display_t!(CreateTypeAs);
2796
2797#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2798pub enum CreateTypeListOptionName {
2799    ElementType,
2800}
2801
2802impl AstDisplay for CreateTypeListOptionName {
2803    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2804        f.write_str(match self {
2805            CreateTypeListOptionName::ElementType => "ELEMENT TYPE",
2806        })
2807    }
2808}
2809
2810impl WithOptionName for CreateTypeListOptionName {
2811    /// # WARNING
2812    ///
2813    /// Whenever implementing this trait consider very carefully whether or not
2814    /// this value could contain sensitive user data. If you're uncertain, err
2815    /// on the conservative side and return `true`.
2816    fn redact_value(&self) -> bool {
2817        match self {
2818            CreateTypeListOptionName::ElementType => false,
2819        }
2820    }
2821}
2822
2823#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2824pub struct CreateTypeListOption<T: AstInfo> {
2825    pub name: CreateTypeListOptionName,
2826    pub value: Option<WithOptionValue<T>>,
2827}
2828impl_display_for_with_option!(CreateTypeListOption);
2829
2830#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2831pub enum CreateTypeMapOptionName {
2832    KeyType,
2833    ValueType,
2834}
2835
2836impl AstDisplay for CreateTypeMapOptionName {
2837    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2838        f.write_str(match self {
2839            CreateTypeMapOptionName::KeyType => "KEY TYPE",
2840            CreateTypeMapOptionName::ValueType => "VALUE TYPE",
2841        })
2842    }
2843}
2844
2845impl WithOptionName for CreateTypeMapOptionName {
2846    /// # WARNING
2847    ///
2848    /// Whenever implementing this trait consider very carefully whether or not
2849    /// this value could contain sensitive user data. If you're uncertain, err
2850    /// on the conservative side and return `true`.
2851    fn redact_value(&self) -> bool {
2852        match self {
2853            CreateTypeMapOptionName::KeyType | CreateTypeMapOptionName::ValueType => false,
2854        }
2855    }
2856}
2857
2858#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2859pub struct CreateTypeMapOption<T: AstInfo> {
2860    pub name: CreateTypeMapOptionName,
2861    pub value: Option<WithOptionValue<T>>,
2862}
2863impl_display_for_with_option!(CreateTypeMapOption);
2864
2865/// `ALTER <OBJECT> ... OWNER TO`
2866#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2867pub struct AlterOwnerStatement<T: AstInfo> {
2868    pub object_type: ObjectType,
2869    pub if_exists: bool,
2870    pub name: UnresolvedObjectName,
2871    pub new_owner: T::RoleName,
2872}
2873
2874impl<T: AstInfo> AstDisplay for AlterOwnerStatement<T> {
2875    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2876        f.write_str("ALTER ");
2877        f.write_node(&self.object_type);
2878        f.write_str(" ");
2879        if self.if_exists {
2880            f.write_str("IF EXISTS ");
2881        }
2882        f.write_node(&self.name);
2883        f.write_str(" OWNER TO ");
2884        f.write_node(&self.new_owner);
2885    }
2886}
2887impl_display_t!(AlterOwnerStatement);
2888
2889/// `ALTER <OBJECT> ... RENAME TO`
2890#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2891pub struct AlterObjectRenameStatement {
2892    pub object_type: ObjectType,
2893    pub if_exists: bool,
2894    pub name: UnresolvedObjectName,
2895    pub to_item_name: Ident,
2896}
2897
2898impl AstDisplay for AlterObjectRenameStatement {
2899    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2900        f.write_str("ALTER ");
2901        f.write_node(&self.object_type);
2902        f.write_str(" ");
2903        if self.if_exists {
2904            f.write_str("IF EXISTS ");
2905        }
2906        f.write_node(&self.name);
2907        f.write_str(" RENAME TO ");
2908        f.write_node(&self.to_item_name);
2909    }
2910}
2911impl_display!(AlterObjectRenameStatement);
2912
2913/// `ALTER <OBJECT> ... [RE]SET (RETAIN HISTORY [FOR ...])`
2914#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2915pub struct AlterRetainHistoryStatement<T: AstInfo> {
2916    pub object_type: ObjectType,
2917    pub if_exists: bool,
2918    pub name: UnresolvedObjectName,
2919    pub history: Option<WithOptionValue<T>>,
2920}
2921
2922impl<T: AstInfo> AstDisplay for AlterRetainHistoryStatement<T> {
2923    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2924        f.write_str("ALTER ");
2925        f.write_node(&self.object_type);
2926        f.write_str(" ");
2927        if self.if_exists {
2928            f.write_str("IF EXISTS ");
2929        }
2930        f.write_node(&self.name);
2931        if let Some(history) = &self.history {
2932            f.write_str(" SET (RETAIN HISTORY ");
2933            f.write_node(history);
2934        } else {
2935            f.write_str(" RESET (RETAIN HISTORY");
2936        }
2937        f.write_str(")");
2938    }
2939}
2940impl_display_t!(AlterRetainHistoryStatement);
2941
2942/// `ALTER <OBJECT> SWAP ...`
2943#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2944pub struct AlterObjectSwapStatement {
2945    pub object_type: ObjectType,
2946    pub if_exists: bool,
2947    pub name_a: UnresolvedObjectName,
2948    pub name_b: Ident,
2949}
2950
2951impl AstDisplay for AlterObjectSwapStatement {
2952    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2953        f.write_str("ALTER ");
2954
2955        f.write_node(&self.object_type);
2956        f.write_str(" ");
2957        if self.if_exists {
2958            f.write_str("IF EXISTS ");
2959        }
2960        f.write_node(&self.name_a);
2961
2962        f.write_str(" SWAP WITH ");
2963        f.write_node(&self.name_b);
2964    }
2965}
2966impl_display!(AlterObjectSwapStatement);
2967
2968#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2969pub enum AlterIndexAction<T: AstInfo> {
2970    SetOptions(Vec<IndexOption<T>>),
2971    ResetOptions(Vec<IndexOptionName>),
2972}
2973
2974/// `ALTER INDEX ... {RESET, SET}`
2975#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2976pub struct AlterIndexStatement<T: AstInfo> {
2977    pub index_name: UnresolvedItemName,
2978    pub if_exists: bool,
2979    pub action: AlterIndexAction<T>,
2980}
2981
2982impl<T: AstInfo> AstDisplay for AlterIndexStatement<T> {
2983    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2984        f.write_str("ALTER INDEX ");
2985        if self.if_exists {
2986            f.write_str("IF EXISTS ");
2987        }
2988        f.write_node(&self.index_name);
2989        f.write_str(" ");
2990
2991        match &self.action {
2992            AlterIndexAction::SetOptions(options) => {
2993                f.write_str("SET (");
2994                f.write_node(&display::comma_separated(options));
2995                f.write_str(")");
2996            }
2997            AlterIndexAction::ResetOptions(options) => {
2998                f.write_str("RESET (");
2999                f.write_node(&display::comma_separated(options));
3000                f.write_str(")");
3001            }
3002        }
3003    }
3004}
3005
3006impl_display_t!(AlterIndexStatement);
3007
3008#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3009pub enum AlterSinkAction<T: AstInfo> {
3010    SetOptions(Vec<CreateSinkOption<T>>),
3011    ResetOptions(Vec<CreateSinkOptionName>),
3012    ChangeRelation(T::ItemName),
3013}
3014
3015#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3016pub struct AlterSinkStatement<T: AstInfo> {
3017    pub sink_name: UnresolvedItemName,
3018    pub if_exists: bool,
3019    pub action: AlterSinkAction<T>,
3020}
3021
3022impl<T: AstInfo> AstDisplay for AlterSinkStatement<T> {
3023    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3024        f.write_str("ALTER SINK ");
3025        if self.if_exists {
3026            f.write_str("IF EXISTS ");
3027        }
3028        f.write_node(&self.sink_name);
3029        f.write_str(" ");
3030
3031        match &self.action {
3032            AlterSinkAction::ChangeRelation(from) => {
3033                f.write_str("SET FROM ");
3034                f.write_node(from);
3035            }
3036            AlterSinkAction::SetOptions(options) => {
3037                f.write_str("SET (");
3038                f.write_node(&display::comma_separated(options));
3039                f.write_str(")");
3040            }
3041            AlterSinkAction::ResetOptions(options) => {
3042                f.write_str("RESET (");
3043                f.write_node(&display::comma_separated(options));
3044                f.write_str(")");
3045            }
3046        }
3047    }
3048}
3049
3050#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3051pub enum AlterSourceAddSubsourceOptionName {
3052    /// Columns whose types you want to unconditionally format as text
3053    TextColumns,
3054    /// Columns you want to ignore when ingesting data
3055    ExcludeColumns,
3056    /// Updated `DETAILS` for an ingestion, e.g.
3057    /// [`crate::ast::PgConfigOptionName::Details`]
3058    /// or
3059    /// [`crate::ast::MySqlConfigOptionName::Details`].
3060    Details,
3061}
3062
3063impl AstDisplay for AlterSourceAddSubsourceOptionName {
3064    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3065        f.write_str(match self {
3066            AlterSourceAddSubsourceOptionName::TextColumns => "TEXT COLUMNS",
3067            AlterSourceAddSubsourceOptionName::ExcludeColumns => "EXCLUDE COLUMNS",
3068            AlterSourceAddSubsourceOptionName::Details => "DETAILS",
3069        })
3070    }
3071}
3072impl_display!(AlterSourceAddSubsourceOptionName);
3073
3074impl WithOptionName for AlterSourceAddSubsourceOptionName {
3075    /// # WARNING
3076    ///
3077    /// Whenever implementing this trait consider very carefully whether or not
3078    /// this value could contain sensitive user data. If you're uncertain, err
3079    /// on the conservative side and return `true`.
3080    fn redact_value(&self) -> bool {
3081        match self {
3082            AlterSourceAddSubsourceOptionName::Details
3083            | AlterSourceAddSubsourceOptionName::TextColumns
3084            | AlterSourceAddSubsourceOptionName::ExcludeColumns => false,
3085        }
3086    }
3087}
3088
3089#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3090/// An option in an `ALTER SOURCE...ADD SUBSOURCE` statement.
3091pub struct AlterSourceAddSubsourceOption<T: AstInfo> {
3092    pub name: AlterSourceAddSubsourceOptionName,
3093    pub value: Option<WithOptionValue<T>>,
3094}
3095impl_display_for_with_option!(AlterSourceAddSubsourceOption);
3096impl_display_t!(AlterSourceAddSubsourceOption);
3097
3098#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3099pub enum AlterSourceAction<T: AstInfo> {
3100    SetOptions(Vec<CreateSourceOption<T>>),
3101    ResetOptions(Vec<CreateSourceOptionName>),
3102    AddSubsources {
3103        external_references: Vec<ExternalReferenceExport>,
3104        options: Vec<AlterSourceAddSubsourceOption<T>>,
3105    },
3106    DropSubsources {
3107        if_exists: bool,
3108        cascade: bool,
3109        names: Vec<UnresolvedItemName>,
3110    },
3111    RefreshReferences,
3112}
3113
3114impl<T: AstInfo> AstDisplay for AlterSourceAction<T> {
3115    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3116    where
3117        W: fmt::Write,
3118    {
3119        match &self {
3120            AlterSourceAction::SetOptions(options) => {
3121                f.write_str("SET (");
3122                f.write_node(&display::comma_separated(options));
3123                f.write_str(")");
3124            }
3125            AlterSourceAction::ResetOptions(options) => {
3126                f.write_str("RESET (");
3127                f.write_node(&display::comma_separated(options));
3128                f.write_str(")");
3129            }
3130            AlterSourceAction::DropSubsources {
3131                if_exists,
3132                cascade,
3133                names,
3134            } => {
3135                f.write_str("DROP SUBSOURCE ");
3136                if *if_exists {
3137                    f.write_str("IF EXISTS ");
3138                }
3139
3140                f.write_node(&display::comma_separated(names));
3141
3142                if *cascade {
3143                    f.write_str(" CASCADE");
3144                }
3145            }
3146            AlterSourceAction::AddSubsources {
3147                external_references: subsources,
3148                options,
3149            } => {
3150                f.write_str("ADD SUBSOURCE ");
3151
3152                f.write_node(&display::comma_separated(subsources));
3153
3154                if !options.is_empty() {
3155                    f.write_str(" WITH (");
3156                    f.write_node(&display::comma_separated(options));
3157                    f.write_str(")");
3158                }
3159            }
3160            AlterSourceAction::RefreshReferences => {
3161                f.write_str("REFRESH REFERENCES");
3162            }
3163        }
3164    }
3165}
3166
3167#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3168pub struct AlterSourceStatement<T: AstInfo> {
3169    pub source_name: UnresolvedItemName,
3170    pub if_exists: bool,
3171    pub action: AlterSourceAction<T>,
3172}
3173
3174impl<T: AstInfo> AstDisplay for AlterSourceStatement<T> {
3175    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3176        f.write_str("ALTER SOURCE ");
3177        if self.if_exists {
3178            f.write_str("IF EXISTS ");
3179        }
3180        f.write_node(&self.source_name);
3181        f.write_str(" ");
3182        f.write_node(&self.action)
3183    }
3184}
3185
3186impl_display_t!(AlterSourceStatement);
3187
3188/// `ALTER SECRET ... AS`
3189#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3190pub struct AlterSecretStatement<T: AstInfo> {
3191    pub name: UnresolvedItemName,
3192    pub if_exists: bool,
3193    pub value: Expr<T>,
3194}
3195
3196impl<T: AstInfo> AstDisplay for AlterSecretStatement<T> {
3197    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3198        f.write_str("ALTER SECRET ");
3199        if self.if_exists {
3200            f.write_str("IF EXISTS ");
3201        }
3202        f.write_node(&self.name);
3203        f.write_str(" AS ");
3204
3205        if f.redacted() {
3206            f.write_str("'<REDACTED>'");
3207        } else {
3208            f.write_node(&self.value);
3209        }
3210    }
3211}
3212
3213impl_display_t!(AlterSecretStatement);
3214
3215#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3216pub enum AlterConnectionAction<T: AstInfo> {
3217    RotateKeys,
3218    SetOption(ConnectionOption<T>),
3219    DropOption(ConnectionOptionName),
3220}
3221
3222impl<T: AstInfo> AstDisplay for AlterConnectionAction<T> {
3223    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3224        match self {
3225            AlterConnectionAction::RotateKeys => f.write_str("ROTATE KEYS"),
3226            AlterConnectionAction::SetOption(option) => {
3227                f.write_str("SET (");
3228                f.write_node(option);
3229                f.write_str(")");
3230            }
3231            AlterConnectionAction::DropOption(option) => {
3232                f.write_str("DROP (");
3233                f.write_node(option);
3234                f.write_str(")");
3235            }
3236        }
3237    }
3238}
3239impl_display_t!(AlterConnectionAction);
3240
3241#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3242pub enum AlterConnectionOptionName {
3243    Validate,
3244}
3245
3246impl AstDisplay for AlterConnectionOptionName {
3247    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3248        f.write_str(match self {
3249            AlterConnectionOptionName::Validate => "VALIDATE",
3250        })
3251    }
3252}
3253impl_display!(AlterConnectionOptionName);
3254
3255impl WithOptionName for AlterConnectionOptionName {
3256    /// # WARNING
3257    ///
3258    /// Whenever implementing this trait consider very carefully whether or not
3259    /// this value could contain sensitive user data. If you're uncertain, err
3260    /// on the conservative side and return `true`.
3261    fn redact_value(&self) -> bool {
3262        match self {
3263            AlterConnectionOptionName::Validate => false,
3264        }
3265    }
3266}
3267
3268#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3269/// An option in an `ALTER CONNECTION...` statement.
3270pub struct AlterConnectionOption<T: AstInfo> {
3271    pub name: AlterConnectionOptionName,
3272    pub value: Option<WithOptionValue<T>>,
3273}
3274impl_display_for_with_option!(AlterConnectionOption);
3275impl_display_t!(AlterConnectionOption);
3276
3277/// `ALTER CONNECTION`
3278#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3279pub struct AlterConnectionStatement<T: AstInfo> {
3280    pub name: UnresolvedItemName,
3281    pub if_exists: bool,
3282    pub actions: Vec<AlterConnectionAction<T>>,
3283    pub with_options: Vec<AlterConnectionOption<T>>,
3284}
3285
3286impl<T: AstInfo> AstDisplay for AlterConnectionStatement<T> {
3287    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3288        f.write_str("ALTER CONNECTION ");
3289        if self.if_exists {
3290            f.write_str("IF EXISTS ");
3291        }
3292        f.write_node(&self.name);
3293        f.write_str(" ");
3294        f.write_node(&display::comma_separated(&self.actions));
3295
3296        if !self.with_options.is_empty() {
3297            f.write_str(" WITH (");
3298            f.write_node(&display::comma_separated(&self.with_options));
3299            f.write_str(")");
3300        }
3301    }
3302}
3303
3304impl_display_t!(AlterConnectionStatement);
3305
3306/// `ALTER ROLE`
3307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3308pub struct AlterRoleStatement<T: AstInfo> {
3309    /// The specified role.
3310    pub name: T::RoleName,
3311    /// Alterations we're making to the role.
3312    pub option: AlterRoleOption,
3313}
3314
3315impl<T: AstInfo> AstDisplay for AlterRoleStatement<T> {
3316    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3317        f.write_str("ALTER ROLE ");
3318        f.write_node(&self.name);
3319        f.write_node(&self.option);
3320    }
3321}
3322impl_display_t!(AlterRoleStatement);
3323
3324/// `ALTER ROLE ... [ WITH | SET ] ...`
3325#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3326pub enum AlterRoleOption {
3327    /// Any options that were attached, in the order they were presented.
3328    Attributes(Vec<RoleAttribute>),
3329    /// A variable that we want to provide a default value for this role.
3330    Variable(SetRoleVar),
3331}
3332
3333impl AstDisplay for AlterRoleOption {
3334    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3335        match self {
3336            AlterRoleOption::Attributes(attrs) => {
3337                for attr in attrs {
3338                    f.write_str(" ");
3339                    attr.fmt(f)
3340                }
3341            }
3342            AlterRoleOption::Variable(var) => {
3343                f.write_str(" ");
3344                f.write_node(var);
3345            }
3346        }
3347    }
3348}
3349impl_display!(AlterRoleOption);
3350
3351/// `ALTER TABLE ... ADD COLUMN ...`
3352#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3353pub struct AlterTableAddColumnStatement<T: AstInfo> {
3354    pub if_exists: bool,
3355    pub name: UnresolvedItemName,
3356    pub if_col_not_exist: bool,
3357    pub column_name: Ident,
3358    pub data_type: T::DataType,
3359}
3360
3361impl<T: AstInfo> AstDisplay for AlterTableAddColumnStatement<T> {
3362    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3363    where
3364        W: fmt::Write,
3365    {
3366        f.write_str("ALTER TABLE ");
3367        if self.if_exists {
3368            f.write_str("IF EXISTS ");
3369        }
3370        f.write_node(&self.name);
3371
3372        f.write_str(" ADD COLUMN ");
3373        if self.if_col_not_exist {
3374            f.write_str("IF NOT EXISTS ");
3375        }
3376
3377        f.write_node(&self.column_name);
3378        f.write_str(" ");
3379        f.write_node(&self.data_type);
3380    }
3381}
3382
3383impl_display_t!(AlterTableAddColumnStatement);
3384
3385/// `ALTER MATERIALIZED VIEW ... APPLY REPLACEMENT ...`
3386#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3387pub struct AlterMaterializedViewApplyReplacementStatement {
3388    pub if_exists: bool,
3389    pub name: UnresolvedItemName,
3390    pub replacement_name: UnresolvedItemName,
3391}
3392
3393impl AstDisplay for AlterMaterializedViewApplyReplacementStatement {
3394    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3395    where
3396        W: fmt::Write,
3397    {
3398        f.write_str("ALTER MATERIALIZED VIEW ");
3399        if self.if_exists {
3400            f.write_str("IF EXISTS ");
3401        }
3402        f.write_node(&self.name);
3403
3404        f.write_str(" APPLY REPLACEMENT ");
3405        f.write_node(&self.replacement_name);
3406    }
3407}
3408
3409impl_display!(AlterMaterializedViewApplyReplacementStatement);
3410
3411#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3412pub struct DiscardStatement {
3413    pub target: DiscardTarget,
3414}
3415
3416impl AstDisplay for DiscardStatement {
3417    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3418        f.write_str("DISCARD ");
3419        f.write_node(&self.target);
3420    }
3421}
3422impl_display!(DiscardStatement);
3423
3424#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3425pub enum DiscardTarget {
3426    Plans,
3427    Sequences,
3428    Temp,
3429    All,
3430}
3431
3432impl AstDisplay for DiscardTarget {
3433    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3434        match self {
3435            DiscardTarget::Plans => f.write_str("PLANS"),
3436            DiscardTarget::Sequences => f.write_str("SEQUENCES"),
3437            DiscardTarget::Temp => f.write_str("TEMP"),
3438            DiscardTarget::All => f.write_str("ALL"),
3439        }
3440    }
3441}
3442impl_display!(DiscardTarget);
3443
3444/// `DROP`
3445#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3446pub struct DropObjectsStatement {
3447    /// The type of the object to drop: TABLE, VIEW, etc.
3448    pub object_type: ObjectType,
3449    /// An optional `IF EXISTS` clause. (Non-standard.)
3450    pub if_exists: bool,
3451    /// One or more objects to drop. (ANSI SQL requires exactly one.)
3452    pub names: Vec<UnresolvedObjectName>,
3453    /// Whether `CASCADE` was specified. This will be `false` when
3454    /// `RESTRICT` was specified.
3455    pub cascade: bool,
3456}
3457
3458impl AstDisplay for DropObjectsStatement {
3459    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3460        f.write_str("DROP ");
3461        f.write_node(&self.object_type);
3462        f.write_str(" ");
3463        if self.if_exists {
3464            f.write_str("IF EXISTS ");
3465        }
3466        f.write_node(&display::comma_separated(&self.names));
3467        if self.cascade && self.object_type != ObjectType::Database {
3468            f.write_str(" CASCADE");
3469        } else if !self.cascade && self.object_type == ObjectType::Database {
3470            f.write_str(" RESTRICT");
3471        }
3472    }
3473}
3474impl_display!(DropObjectsStatement);
3475
3476/// `DROP OWNED BY ...`
3477#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3478pub struct DropOwnedStatement<T: AstInfo> {
3479    /// The roles whose owned objects are being dropped.
3480    pub role_names: Vec<T::RoleName>,
3481    /// Whether `CASCADE` was specified. `false` for `RESTRICT` and `None` if no drop behavior at
3482    /// all was specified.
3483    pub cascade: Option<bool>,
3484}
3485
3486impl<T: AstInfo> DropOwnedStatement<T> {
3487    pub fn cascade(&self) -> bool {
3488        self.cascade == Some(true)
3489    }
3490}
3491
3492impl<T: AstInfo> AstDisplay for DropOwnedStatement<T> {
3493    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3494        f.write_str("DROP OWNED BY ");
3495        f.write_node(&display::comma_separated(&self.role_names));
3496        if let Some(true) = self.cascade {
3497            f.write_str(" CASCADE");
3498        } else if let Some(false) = self.cascade {
3499            f.write_str(" RESTRICT");
3500        }
3501    }
3502}
3503impl_display_t!(DropOwnedStatement);
3504
3505#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3506pub struct QualifiedReplica {
3507    pub cluster: Ident,
3508    pub replica: Ident,
3509}
3510
3511impl AstDisplay for QualifiedReplica {
3512    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3513        f.write_node(&self.cluster);
3514        f.write_str(".");
3515        f.write_node(&self.replica);
3516    }
3517}
3518impl_display!(QualifiedReplica);
3519
3520/// `SET <variable>`
3521///
3522/// Note: this is not a standard SQL statement, but it is supported by at
3523/// least MySQL and PostgreSQL. Not all MySQL-specific syntactic forms are
3524/// supported yet.
3525#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3526pub struct SetVariableStatement {
3527    pub local: bool,
3528    pub variable: Ident,
3529    pub to: SetVariableTo,
3530}
3531
3532impl AstDisplay for SetVariableStatement {
3533    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3534        f.write_str("SET ");
3535        if self.local {
3536            f.write_str("LOCAL ");
3537        }
3538        f.write_node(&self.variable);
3539        f.write_str(" = ");
3540        f.write_node(&self.to);
3541    }
3542}
3543impl_display!(SetVariableStatement);
3544
3545/// `RESET <variable>`
3546///
3547/// Note: this is not a standard SQL statement, but it is supported by at
3548/// least MySQL and PostgreSQL. Not all syntactic forms are supported yet.
3549#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3550pub struct ResetVariableStatement {
3551    pub variable: Ident,
3552}
3553
3554impl AstDisplay for ResetVariableStatement {
3555    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3556        f.write_str("RESET ");
3557        f.write_node(&self.variable);
3558    }
3559}
3560impl_display!(ResetVariableStatement);
3561
3562/// `SHOW <variable>`
3563#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3564pub struct ShowVariableStatement {
3565    pub variable: Ident,
3566}
3567
3568impl AstDisplay for ShowVariableStatement {
3569    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3570        f.write_str("SHOW ");
3571        f.write_node(&self.variable);
3572    }
3573}
3574impl_display!(ShowVariableStatement);
3575
3576/// `INSPECT SHARD <id>`
3577#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3578pub struct InspectShardStatement {
3579    pub id: String,
3580}
3581
3582impl AstDisplay for InspectShardStatement {
3583    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3584        f.write_str("INSPECT SHARD ");
3585        f.write_str("'");
3586        f.write_node(&display::escape_single_quote_string(&self.id));
3587        f.write_str("'");
3588    }
3589}
3590impl_display!(InspectShardStatement);
3591
3592#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3593pub enum ShowObjectType<T: AstInfo> {
3594    MaterializedView {
3595        in_cluster: Option<T::ClusterName>,
3596    },
3597    Index {
3598        in_cluster: Option<T::ClusterName>,
3599        on_object: Option<T::ItemName>,
3600    },
3601    Table {
3602        on_source: Option<T::ItemName>,
3603    },
3604    View,
3605    Source {
3606        in_cluster: Option<T::ClusterName>,
3607    },
3608    Sink {
3609        in_cluster: Option<T::ClusterName>,
3610    },
3611    MetricSink {
3612        in_cluster: Option<T::ClusterName>,
3613    },
3614    Type,
3615    Role,
3616    Cluster,
3617    ClusterReplica,
3618    Object,
3619    Secret,
3620    Connection,
3621    Database,
3622    Schema {
3623        from: Option<T::DatabaseName>,
3624    },
3625    Subsource {
3626        on_source: Option<T::ItemName>,
3627    },
3628    Privileges {
3629        object_type: Option<SystemObjectType>,
3630        role: Option<T::RoleName>,
3631    },
3632    DefaultPrivileges {
3633        object_type: Option<ObjectType>,
3634        role: Option<T::RoleName>,
3635    },
3636    RoleMembership {
3637        role: Option<T::RoleName>,
3638    },
3639    NetworkPolicy,
3640}
3641/// `SHOW <object>S`
3642///
3643/// ```sql
3644/// SHOW TABLES;
3645/// SHOW SOURCES;
3646/// SHOW VIEWS;
3647/// SHOW SINKS;
3648/// ```
3649#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3650pub struct ShowObjectsStatement<T: AstInfo> {
3651    pub object_type: ShowObjectType<T>,
3652    pub from: Option<T::SchemaName>,
3653    pub filter: Option<ShowStatementFilter<T>>,
3654}
3655
3656impl<T: AstInfo> AstDisplay for ShowObjectsStatement<T> {
3657    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3658        f.write_str("SHOW");
3659        f.write_str(" ");
3660
3661        f.write_str(match &self.object_type {
3662            ShowObjectType::Table { .. } => "TABLES",
3663            ShowObjectType::View => "VIEWS",
3664            ShowObjectType::Source { .. } => "SOURCES",
3665            ShowObjectType::Sink { .. } => "SINKS",
3666            ShowObjectType::MetricSink { .. } => "METRIC SINKS",
3667            ShowObjectType::Type => "TYPES",
3668            ShowObjectType::Role => "ROLES",
3669            ShowObjectType::Cluster => "CLUSTERS",
3670            ShowObjectType::ClusterReplica => "CLUSTER REPLICAS",
3671            ShowObjectType::Object => "OBJECTS",
3672            ShowObjectType::Secret => "SECRETS",
3673            ShowObjectType::Connection => "CONNECTIONS",
3674            ShowObjectType::MaterializedView { .. } => "MATERIALIZED VIEWS",
3675            ShowObjectType::Index { .. } => "INDEXES",
3676            ShowObjectType::Database => "DATABASES",
3677            ShowObjectType::Schema { .. } => "SCHEMAS",
3678            ShowObjectType::Subsource { .. } => "SUBSOURCES",
3679            ShowObjectType::Privileges { .. } => "PRIVILEGES",
3680            ShowObjectType::DefaultPrivileges { .. } => "DEFAULT PRIVILEGES",
3681            ShowObjectType::RoleMembership { .. } => "ROLE MEMBERSHIP",
3682            ShowObjectType::NetworkPolicy => "NETWORK POLICIES",
3683        });
3684
3685        if let ShowObjectType::Index { on_object, .. } = &self.object_type {
3686            if let Some(on_object) = on_object {
3687                f.write_str(" ON ");
3688                f.write_node(on_object);
3689            }
3690        }
3691
3692        if let ShowObjectType::Schema { from: Some(from) } = &self.object_type {
3693            f.write_str(" FROM ");
3694            f.write_node(from);
3695        }
3696
3697        if let Some(from) = &self.from {
3698            f.write_str(" FROM ");
3699            f.write_node(from);
3700        }
3701
3702        // append IN CLUSTER clause
3703        match &self.object_type {
3704            ShowObjectType::MaterializedView { in_cluster }
3705            | ShowObjectType::Index { in_cluster, .. }
3706            | ShowObjectType::Sink { in_cluster }
3707            | ShowObjectType::MetricSink { in_cluster }
3708            | ShowObjectType::Source { in_cluster } => {
3709                if let Some(cluster) = in_cluster {
3710                    f.write_str(" IN CLUSTER ");
3711                    f.write_node(cluster);
3712                }
3713            }
3714            _ => (),
3715        }
3716
3717        if let ShowObjectType::Subsource { on_source } = &self.object_type {
3718            if let Some(on_source) = on_source {
3719                f.write_str(" ON ");
3720                f.write_node(on_source);
3721            }
3722        }
3723
3724        if let ShowObjectType::Table { on_source } = &self.object_type {
3725            if let Some(on_source) = on_source {
3726                f.write_str(" ON ");
3727                f.write_node(on_source);
3728            }
3729        }
3730
3731        if let ShowObjectType::Privileges { object_type, role } = &self.object_type {
3732            if let Some(object_type) = object_type {
3733                f.write_str(" ON ");
3734                f.write_node(object_type);
3735                if let SystemObjectType::Object(_) = object_type {
3736                    f.write_str("S");
3737                }
3738            }
3739            if let Some(role) = role {
3740                f.write_str(" FOR ");
3741                f.write_node(role);
3742            }
3743        }
3744
3745        if let ShowObjectType::DefaultPrivileges { object_type, role } = &self.object_type {
3746            if let Some(object_type) = object_type {
3747                f.write_str(" ON ");
3748                f.write_node(object_type);
3749                f.write_str("S");
3750            }
3751            if let Some(role) = role {
3752                f.write_str(" FOR ");
3753                f.write_node(role);
3754            }
3755        }
3756
3757        if let ShowObjectType::RoleMembership {
3758            role: Some(role), ..
3759        } = &self.object_type
3760        {
3761            f.write_str(" FOR ");
3762            f.write_node(role);
3763        }
3764
3765        if let Some(filter) = &self.filter {
3766            f.write_str(" ");
3767            f.write_node(filter);
3768        }
3769    }
3770}
3771impl_display_t!(ShowObjectsStatement);
3772
3773/// `SHOW COLUMNS`
3774///
3775/// Note: this is a MySQL-specific statement.
3776#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3777pub struct ShowColumnsStatement<T: AstInfo> {
3778    pub table_name: T::ItemName,
3779    pub filter: Option<ShowStatementFilter<T>>,
3780}
3781
3782impl<T: AstInfo> AstDisplay for ShowColumnsStatement<T> {
3783    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3784        f.write_str("SHOW ");
3785        f.write_str("COLUMNS FROM ");
3786        f.write_node(&self.table_name);
3787        if let Some(filter) = &self.filter {
3788            f.write_str(" ");
3789            f.write_node(filter);
3790        }
3791    }
3792}
3793impl_display_t!(ShowColumnsStatement);
3794
3795/// `SHOW [REDACTED] CREATE VIEW <view>`
3796#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3797pub struct ShowCreateViewStatement<T: AstInfo> {
3798    pub view_name: T::ItemName,
3799    pub redacted: bool,
3800}
3801
3802impl<T: AstInfo> AstDisplay for ShowCreateViewStatement<T> {
3803    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3804        f.write_str("SHOW ");
3805        if self.redacted {
3806            f.write_str("REDACTED ");
3807        }
3808        f.write_str("CREATE VIEW ");
3809        f.write_node(&self.view_name);
3810    }
3811}
3812impl_display_t!(ShowCreateViewStatement);
3813
3814/// `SHOW [REDACTED] CREATE MATERIALIZED VIEW <name>`
3815#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3816pub struct ShowCreateMaterializedViewStatement<T: AstInfo> {
3817    pub materialized_view_name: T::ItemName,
3818    pub redacted: bool,
3819}
3820
3821impl<T: AstInfo> AstDisplay for ShowCreateMaterializedViewStatement<T> {
3822    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3823        f.write_str("SHOW ");
3824        if self.redacted {
3825            f.write_str("REDACTED ");
3826        }
3827        f.write_str("CREATE MATERIALIZED VIEW ");
3828        f.write_node(&self.materialized_view_name);
3829    }
3830}
3831impl_display_t!(ShowCreateMaterializedViewStatement);
3832
3833/// `SHOW [REDACTED] CREATE SOURCE <source>`
3834#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3835pub struct ShowCreateSourceStatement<T: AstInfo> {
3836    pub source_name: T::ItemName,
3837    pub redacted: bool,
3838}
3839
3840impl<T: AstInfo> AstDisplay for ShowCreateSourceStatement<T> {
3841    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3842        f.write_str("SHOW ");
3843        if self.redacted {
3844            f.write_str("REDACTED ");
3845        }
3846        f.write_str("CREATE SOURCE ");
3847        f.write_node(&self.source_name);
3848    }
3849}
3850impl_display_t!(ShowCreateSourceStatement);
3851
3852/// `SHOW [REDACTED] CREATE TABLE <table>`
3853#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3854pub struct ShowCreateTableStatement<T: AstInfo> {
3855    pub table_name: T::ItemName,
3856    pub redacted: bool,
3857}
3858
3859impl<T: AstInfo> AstDisplay for ShowCreateTableStatement<T> {
3860    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3861        f.write_str("SHOW ");
3862        if self.redacted {
3863            f.write_str("REDACTED ");
3864        }
3865        f.write_str("CREATE TABLE ");
3866        f.write_node(&self.table_name);
3867    }
3868}
3869impl_display_t!(ShowCreateTableStatement);
3870
3871/// `SHOW [REDACTED] CREATE SINK <sink>`
3872#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3873pub struct ShowCreateSinkStatement<T: AstInfo> {
3874    pub sink_name: T::ItemName,
3875    pub redacted: bool,
3876}
3877
3878impl<T: AstInfo> AstDisplay for ShowCreateSinkStatement<T> {
3879    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3880        f.write_str("SHOW ");
3881        if self.redacted {
3882            f.write_str("REDACTED ");
3883        }
3884        f.write_str("CREATE SINK ");
3885        f.write_node(&self.sink_name);
3886    }
3887}
3888impl_display_t!(ShowCreateSinkStatement);
3889
3890/// `SHOW [REDACTED] CREATE METRIC SINK <sink>`
3891#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3892pub struct ShowCreateMetricSinkStatement<T: AstInfo> {
3893    pub metric_sink_name: T::ItemName,
3894    pub redacted: bool,
3895}
3896
3897impl<T: AstInfo> AstDisplay for ShowCreateMetricSinkStatement<T> {
3898    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3899        f.write_str("SHOW ");
3900        if self.redacted {
3901            f.write_str("REDACTED ");
3902        }
3903        f.write_str("CREATE METRIC SINK ");
3904        f.write_node(&self.metric_sink_name);
3905    }
3906}
3907impl_display_t!(ShowCreateMetricSinkStatement);
3908
3909/// `SHOW [REDACTED] CREATE INDEX <index>`
3910#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3911pub struct ShowCreateIndexStatement<T: AstInfo> {
3912    pub index_name: T::ItemName,
3913    pub redacted: bool,
3914}
3915
3916impl<T: AstInfo> AstDisplay for ShowCreateIndexStatement<T> {
3917    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3918        f.write_str("SHOW ");
3919        if self.redacted {
3920            f.write_str("REDACTED ");
3921        }
3922        f.write_str("CREATE INDEX ");
3923        f.write_node(&self.index_name);
3924    }
3925}
3926impl_display_t!(ShowCreateIndexStatement);
3927
3928/// `SHOW [REDACTED] CREATE CONNECTION <connection>`
3929#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3930pub struct ShowCreateConnectionStatement<T: AstInfo> {
3931    pub connection_name: T::ItemName,
3932    pub redacted: bool,
3933}
3934
3935impl<T: AstInfo> AstDisplay for ShowCreateConnectionStatement<T> {
3936    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3937        f.write_str("SHOW ");
3938        if self.redacted {
3939            f.write_str("REDACTED ");
3940        }
3941        f.write_str("CREATE CONNECTION ");
3942        f.write_node(&self.connection_name);
3943    }
3944}
3945
3946#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3947pub struct ShowCreateClusterStatement<T: AstInfo> {
3948    pub cluster_name: T::ClusterName,
3949}
3950
3951impl<T: AstInfo> AstDisplay for ShowCreateClusterStatement<T> {
3952    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3953        f.write_str("SHOW CREATE CLUSTER ");
3954        f.write_node(&self.cluster_name);
3955    }
3956}
3957
3958/// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...`
3959#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3960pub struct StartTransactionStatement {
3961    pub modes: Vec<TransactionMode>,
3962}
3963
3964impl AstDisplay for StartTransactionStatement {
3965    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3966        f.write_str("START TRANSACTION");
3967        if !self.modes.is_empty() {
3968            f.write_str(" ");
3969            f.write_node(&display::comma_separated(&self.modes));
3970        }
3971    }
3972}
3973impl_display!(StartTransactionStatement);
3974
3975/// `SHOW [REDACTED] CREATE TYPE <type>`
3976#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3977pub struct ShowCreateTypeStatement<T: AstInfo> {
3978    pub type_name: T::DataType,
3979    pub redacted: bool,
3980}
3981
3982impl<T: AstInfo> AstDisplay for ShowCreateTypeStatement<T> {
3983    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3984        f.write_str("SHOW ");
3985        if self.redacted {
3986            f.write_str("REDACTED ");
3987        }
3988        f.write_str("CREATE TYPE ");
3989        f.write_node(&self.type_name);
3990    }
3991}
3992
3993/// `SET TRANSACTION ...`
3994#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3995pub struct SetTransactionStatement {
3996    pub local: bool,
3997    pub modes: Vec<TransactionMode>,
3998}
3999
4000impl AstDisplay for SetTransactionStatement {
4001    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4002        f.write_str("SET ");
4003        if !self.local {
4004            f.write_str("SESSION CHARACTERISTICS AS ");
4005        }
4006        f.write_str("TRANSACTION");
4007        if !self.modes.is_empty() {
4008            f.write_str(" ");
4009            f.write_node(&display::comma_separated(&self.modes));
4010        }
4011    }
4012}
4013impl_display!(SetTransactionStatement);
4014
4015/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
4016#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4017pub struct CommitStatement {
4018    pub chain: bool,
4019}
4020
4021impl AstDisplay for CommitStatement {
4022    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4023        f.write_str("COMMIT");
4024        if self.chain {
4025            f.write_str(" AND CHAIN");
4026        }
4027    }
4028}
4029impl_display!(CommitStatement);
4030
4031/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
4032#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4033pub struct RollbackStatement {
4034    pub chain: bool,
4035}
4036
4037impl AstDisplay for RollbackStatement {
4038    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4039        f.write_str("ROLLBACK");
4040        if self.chain {
4041            f.write_str(" AND CHAIN");
4042        }
4043    }
4044}
4045impl_display!(RollbackStatement);
4046
4047#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4048pub enum SubscribeOptionName {
4049    Snapshot,
4050    Progress,
4051}
4052
4053impl AstDisplay for SubscribeOptionName {
4054    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4055        match self {
4056            SubscribeOptionName::Snapshot => f.write_str("SNAPSHOT"),
4057            SubscribeOptionName::Progress => f.write_str("PROGRESS"),
4058        }
4059    }
4060}
4061impl_display!(SubscribeOptionName);
4062
4063impl WithOptionName for SubscribeOptionName {
4064    /// # WARNING
4065    ///
4066    /// Whenever implementing this trait consider very carefully whether or not
4067    /// this value could contain sensitive user data. If you're uncertain, err
4068    /// on the conservative side and return `true`.
4069    fn redact_value(&self) -> bool {
4070        match self {
4071            SubscribeOptionName::Snapshot | SubscribeOptionName::Progress => false,
4072        }
4073    }
4074}
4075
4076#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4077pub struct SubscribeOption<T: AstInfo> {
4078    pub name: SubscribeOptionName,
4079    pub value: Option<WithOptionValue<T>>,
4080}
4081impl_display_for_with_option!(SubscribeOption);
4082impl_display_t!(SubscribeOption);
4083
4084/// `SUBSCRIBE`
4085#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4086pub struct SubscribeStatement<T: AstInfo> {
4087    pub relation: SubscribeRelation<T>,
4088    pub options: Vec<SubscribeOption<T>>,
4089    pub as_of: Option<AsOf<T>>,
4090    pub up_to: Option<Expr<T>>,
4091    pub output: SubscribeOutput<T>,
4092}
4093
4094impl<T: AstInfo> AstDisplay for SubscribeStatement<T> {
4095    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4096        f.write_str("SUBSCRIBE ");
4097        if self.relation.needs_explicit_to(f.simple()) {
4098            // Without the optional `TO` keyword, a relation whose first name
4099            // component is the bare keyword `to` (e.g. `SUBSCRIBE TO to`) would
4100            // display as `SUBSCRIBE to`, which re-parses with `to` consumed as
4101            // the optional keyword, dropping the relation name.
4102            f.write_str("TO ");
4103        }
4104        f.write_node(&self.relation);
4105        if !self.options.is_empty() {
4106            f.write_str(" WITH (");
4107            f.write_node(&display::comma_separated(&self.options));
4108            f.write_str(")");
4109        }
4110        if let Some(as_of) = &self.as_of {
4111            f.write_str(" ");
4112            f.write_node(as_of);
4113        }
4114        if let Some(up_to) = &self.up_to {
4115            f.write_str(" UP TO ");
4116            f.write_node(up_to);
4117        }
4118        f.write_str(&self.output);
4119    }
4120}
4121impl_display_t!(SubscribeStatement);
4122
4123#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4124pub enum SubscribeRelation<T: AstInfo> {
4125    Name(T::ItemName),
4126    Query(Query<T>),
4127}
4128
4129impl<T: AstInfo> SubscribeRelation<T> {
4130    /// Reports whether printing this relation after `SUBSCRIBE` requires the
4131    /// optional `TO` keyword to avoid reparsing the first name component as that
4132    /// keyword instead of as part of the relation name.
4133    pub fn needs_explicit_to(&self, bare_identifiers: bool) -> bool {
4134        let SubscribeRelation::Name(name) = self else {
4135            return false;
4136        };
4137        bare_identifiers && name_starts_with_bare_to(&name.to_ast_string_simple())
4138    }
4139}
4140
4141fn name_starts_with_bare_to(name: &str) -> bool {
4142    let Some(rest) = name.strip_prefix("to") else {
4143        return false;
4144    };
4145    rest.is_empty() || rest.starts_with('.')
4146}
4147
4148impl<T: AstInfo> AstDisplay for SubscribeRelation<T> {
4149    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4150        match self {
4151            SubscribeRelation::Name(name) => f.write_node(name),
4152            SubscribeRelation::Query(query) => {
4153                f.write_str("(");
4154                f.write_node(query);
4155                f.write_str(")");
4156            }
4157        }
4158    }
4159}
4160impl_display_t!(SubscribeRelation);
4161
4162#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4163pub struct ExplainPlanStatement<T: AstInfo> {
4164    pub stage: Option<ExplainStage>,
4165    pub with_options: Vec<ExplainPlanOption<T>>,
4166    pub format: Option<ExplainFormat>,
4167    pub explainee: Explainee<T>,
4168}
4169
4170impl<T: AstInfo> ExplainPlanStatement<T> {
4171    pub fn stage(&self) -> ExplainStage {
4172        self.stage.unwrap_or(ExplainStage::PhysicalPlan)
4173    }
4174
4175    pub fn format(&self) -> ExplainFormat {
4176        self.format.unwrap_or(ExplainFormat::Text)
4177    }
4178}
4179
4180impl<T: AstInfo> AstDisplay for ExplainPlanStatement<T> {
4181    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4182        f.write_str("EXPLAIN");
4183        if let Some(stage) = &self.stage {
4184            f.write_str(" ");
4185            f.write_node(stage);
4186        }
4187        if !self.with_options.is_empty() {
4188            f.write_str(" WITH (");
4189            f.write_node(&display::comma_separated(&self.with_options));
4190            f.write_str(")");
4191        }
4192        if let Some(format) = &self.format {
4193            f.write_str(" AS ");
4194            f.write_node(format);
4195        }
4196        if self.stage.is_some() {
4197            f.write_str(" FOR");
4198        }
4199        f.write_str(" ");
4200        f.write_node(&self.explainee);
4201    }
4202}
4203impl_display_t!(ExplainPlanStatement);
4204
4205// Note: the `AstDisplay` implementation and `Parser::parse_` method for this
4206// enum are generated automatically by this crate's `build.rs`.
4207#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4208pub enum ExplainPlanOptionName {
4209    Arity,
4210    Cardinality,
4211    ColumnNames,
4212    FilterPushdown,
4213    HumanizedExpressions,
4214    JoinImplementations,
4215    Keys,
4216    LinearChains,
4217    NonNegative,
4218    NoFastPath,
4219    NoNotices,
4220    NodeIdentifiers,
4221    RawPlans,
4222    RawSyntax,
4223    Raw, // Listed after the `Raw~` variants to keep the parser happy!
4224    Redacted,
4225    SubtreeSize,
4226    Timing,
4227    Types,
4228    Equivalences,
4229    ReoptimizeImportedViews,
4230    EnableNewOuterJoinLowering,
4231    EnableEagerDeltaJoins,
4232    EnableVariadicLeftJoinLowering,
4233    EnableLetrecFixpointAnalysis,
4234    EnableJoinPrioritizeArranged,
4235    EnableProjectionPushdownAfterRelationCse,
4236    EnableFixedCorrelatedCteLowering,
4237    EnableUnionCancellationAfterRelationCse,
4238}
4239
4240impl WithOptionName for ExplainPlanOptionName {
4241    /// # WARNING
4242    ///
4243    /// Whenever implementing this trait consider very carefully whether or not
4244    /// this value could contain sensitive user data. If you're uncertain, err
4245    /// on the conservative side and return `true`.
4246    fn redact_value(&self) -> bool {
4247        match self {
4248            Self::Arity
4249            | Self::Cardinality
4250            | Self::ColumnNames
4251            | Self::FilterPushdown
4252            | Self::HumanizedExpressions
4253            | Self::JoinImplementations
4254            | Self::Keys
4255            | Self::LinearChains
4256            | Self::NonNegative
4257            | Self::NoFastPath
4258            | Self::NoNotices
4259            | Self::NodeIdentifiers
4260            | Self::RawPlans
4261            | Self::RawSyntax
4262            | Self::Raw
4263            | Self::Redacted
4264            | Self::SubtreeSize
4265            | Self::Timing
4266            | Self::Types
4267            | Self::Equivalences
4268            | Self::ReoptimizeImportedViews
4269            | Self::EnableNewOuterJoinLowering
4270            | Self::EnableEagerDeltaJoins
4271            | Self::EnableVariadicLeftJoinLowering
4272            | Self::EnableLetrecFixpointAnalysis
4273            | Self::EnableJoinPrioritizeArranged
4274            | Self::EnableProjectionPushdownAfterRelationCse
4275            | Self::EnableFixedCorrelatedCteLowering
4276            | Self::EnableUnionCancellationAfterRelationCse => false,
4277        }
4278    }
4279}
4280
4281#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4282pub struct ExplainPlanOption<T: AstInfo> {
4283    pub name: ExplainPlanOptionName,
4284    pub value: Option<WithOptionValue<T>>,
4285}
4286impl_display_for_with_option!(ExplainPlanOption);
4287
4288#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4289pub enum ExplainSinkSchemaFor {
4290    Key,
4291    Value,
4292}
4293#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4294pub struct ExplainSinkSchemaStatement<T: AstInfo> {
4295    pub schema_for: ExplainSinkSchemaFor,
4296    pub format: Option<ExplainFormat>,
4297    pub statement: CreateSinkStatement<T>,
4298}
4299
4300impl<T: AstInfo> AstDisplay for ExplainSinkSchemaStatement<T> {
4301    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4302        f.write_str("EXPLAIN ");
4303        match &self.schema_for {
4304            ExplainSinkSchemaFor::Key => f.write_str("KEY"),
4305            ExplainSinkSchemaFor::Value => f.write_str("VALUE"),
4306        }
4307        f.write_str(" SCHEMA");
4308        if let Some(format) = &self.format {
4309            f.write_str(" AS ");
4310            f.write_node(format);
4311        }
4312        f.write_str(" FOR ");
4313        f.write_node(&self.statement);
4314    }
4315}
4316impl_display_t!(ExplainSinkSchemaStatement);
4317
4318#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4319pub struct ExplainPushdownStatement<T: AstInfo> {
4320    pub explainee: Explainee<T>,
4321}
4322
4323impl<T: AstInfo> AstDisplay for ExplainPushdownStatement<T> {
4324    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4325        f.write_str("EXPLAIN FILTER PUSHDOWN FOR ");
4326        f.write_node(&self.explainee);
4327    }
4328}
4329impl_display_t!(ExplainPushdownStatement);
4330
4331#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4332pub enum ExplainAnalyzeComputationProperty {
4333    Cpu,
4334    Memory,
4335}
4336
4337#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4338pub enum ExplainAnalyzeProperty {
4339    Computation(ExplainAnalyzeComputationProperties),
4340    Hints,
4341}
4342
4343#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4344pub struct ExplainAnalyzeComputationProperties {
4345    /// Must be non-empty.
4346    pub properties: Vec<ExplainAnalyzeComputationProperty>,
4347    pub skew: bool,
4348}
4349#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4350pub struct ExplainAnalyzeObjectStatement<T: AstInfo> {
4351    pub properties: ExplainAnalyzeProperty,
4352    /// Should only be `Explainee::Index` or `Explainee::MaterializedView`
4353    pub explainee: Explainee<T>,
4354    pub as_sql: bool,
4355}
4356
4357impl<T: AstInfo> AstDisplay for ExplainAnalyzeObjectStatement<T> {
4358    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4359        f.write_str("EXPLAIN ANALYZE");
4360        match &self.properties {
4361            ExplainAnalyzeProperty::Computation(ExplainAnalyzeComputationProperties {
4362                properties,
4363                skew,
4364            }) => {
4365                let mut first = true;
4366                for property in properties {
4367                    if first {
4368                        first = false;
4369                    } else {
4370                        f.write_str(",");
4371                    }
4372                    match property {
4373                        ExplainAnalyzeComputationProperty::Cpu => f.write_str(" CPU"),
4374                        ExplainAnalyzeComputationProperty::Memory => f.write_str(" MEMORY"),
4375                    }
4376                }
4377                if *skew {
4378                    f.write_str(" WITH SKEW");
4379                }
4380            }
4381            ExplainAnalyzeProperty::Hints => f.write_str(" HINTS"),
4382        }
4383        f.write_str(" FOR ");
4384        f.write_node(&self.explainee);
4385        if self.as_sql {
4386            f.write_str(" AS SQL");
4387        }
4388    }
4389}
4390impl_display_t!(ExplainAnalyzeObjectStatement);
4391
4392#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4393pub struct ExplainAnalyzeClusterStatement {
4394    pub properties: ExplainAnalyzeComputationProperties,
4395    pub as_sql: bool,
4396}
4397
4398impl AstDisplay for ExplainAnalyzeClusterStatement {
4399    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4400        f.write_str("EXPLAIN ANALYZE CLUSTER");
4401
4402        let mut first = true;
4403        for property in &self.properties.properties {
4404            if first {
4405                first = false;
4406            } else {
4407                f.write_str(",");
4408            }
4409            match property {
4410                ExplainAnalyzeComputationProperty::Cpu => f.write_str(" CPU"),
4411                ExplainAnalyzeComputationProperty::Memory => f.write_str(" MEMORY"),
4412            }
4413        }
4414
4415        if self.properties.skew {
4416            f.write_str(" WITH SKEW");
4417        }
4418        if self.as_sql {
4419            f.write_str(" AS SQL");
4420        }
4421    }
4422}
4423impl_display!(ExplainAnalyzeClusterStatement);
4424
4425#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4426pub struct ExplainTimestampStatement<T: AstInfo> {
4427    pub format: Option<ExplainFormat>,
4428    pub select: SelectStatement<T>,
4429}
4430
4431impl<T: AstInfo> ExplainTimestampStatement<T> {
4432    pub fn format(&self) -> ExplainFormat {
4433        self.format.unwrap_or(ExplainFormat::Text)
4434    }
4435}
4436
4437impl<T: AstInfo> AstDisplay for ExplainTimestampStatement<T> {
4438    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4439        f.write_str("EXPLAIN TIMESTAMP");
4440        if let Some(format) = &self.format {
4441            f.write_str(" AS ");
4442            f.write_node(format);
4443        }
4444        f.write_str(" FOR ");
4445        f.write_node(&self.select);
4446    }
4447}
4448impl_display_t!(ExplainTimestampStatement);
4449
4450#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4451pub enum InsertSource<T: AstInfo> {
4452    Query(Query<T>),
4453    DefaultValues,
4454}
4455
4456impl<T: AstInfo> AstDisplay for InsertSource<T> {
4457    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4458        match self {
4459            InsertSource::Query(query) => f.write_node(query),
4460            InsertSource::DefaultValues => f.write_str("DEFAULT VALUES"),
4461        }
4462    }
4463}
4464impl_display_t!(InsertSource);
4465
4466#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
4467pub enum ObjectType {
4468    Table,
4469    View,
4470    MaterializedView,
4471    Source,
4472    Sink,
4473    MetricSink,
4474    Index,
4475    Type,
4476    Role,
4477    Cluster,
4478    ClusterReplica,
4479    Secret,
4480    Connection,
4481    Database,
4482    Schema,
4483    Func,
4484    Subsource,
4485    NetworkPolicy,
4486}
4487
4488impl ObjectType {
4489    pub fn lives_in_schema(&self) -> bool {
4490        match self {
4491            ObjectType::Table
4492            | ObjectType::View
4493            | ObjectType::MaterializedView
4494            | ObjectType::Source
4495            | ObjectType::Sink
4496            | ObjectType::MetricSink
4497            | ObjectType::Index
4498            | ObjectType::Type
4499            | ObjectType::Secret
4500            | ObjectType::Connection
4501            | ObjectType::Func
4502            | ObjectType::Subsource => true,
4503            ObjectType::Database
4504            | ObjectType::Schema
4505            | ObjectType::Cluster
4506            | ObjectType::ClusterReplica
4507            | ObjectType::Role
4508            | ObjectType::NetworkPolicy => false,
4509        }
4510    }
4511}
4512
4513impl AstDisplay for ObjectType {
4514    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4515        f.write_str(match self {
4516            ObjectType::Table => "TABLE",
4517            ObjectType::View => "VIEW",
4518            ObjectType::MaterializedView => "MATERIALIZED VIEW",
4519            ObjectType::Source => "SOURCE",
4520            ObjectType::Sink => "SINK",
4521            ObjectType::MetricSink => "METRIC SINK",
4522            ObjectType::Index => "INDEX",
4523            ObjectType::Type => "TYPE",
4524            ObjectType::Role => "ROLE",
4525            ObjectType::Cluster => "CLUSTER",
4526            ObjectType::ClusterReplica => "CLUSTER REPLICA",
4527            ObjectType::Secret => "SECRET",
4528            ObjectType::Connection => "CONNECTION",
4529            ObjectType::Database => "DATABASE",
4530            ObjectType::Schema => "SCHEMA",
4531            ObjectType::Func => "FUNCTION",
4532            ObjectType::Subsource => "SUBSOURCE",
4533            ObjectType::NetworkPolicy => "NETWORK POLICY",
4534        })
4535    }
4536}
4537impl_display!(ObjectType);
4538
4539#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
4540pub enum SystemObjectType {
4541    System,
4542    Object(ObjectType),
4543}
4544
4545impl AstDisplay for SystemObjectType {
4546    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4547        match self {
4548            SystemObjectType::System => f.write_str("SYSTEM"),
4549            SystemObjectType::Object(object) => f.write_node(object),
4550        }
4551    }
4552}
4553impl_display!(SystemObjectType);
4554
4555#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4556pub enum ShowStatementFilter<T: AstInfo> {
4557    Like(String),
4558    Where(Expr<T>),
4559}
4560
4561impl<T: AstInfo> AstDisplay for ShowStatementFilter<T> {
4562    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4563        use ShowStatementFilter::*;
4564        match self {
4565            Like(pattern) => {
4566                f.write_str("LIKE '");
4567                f.write_node(&display::escape_single_quote_string(pattern));
4568                f.write_str("'");
4569            }
4570            Where(expr) => {
4571                f.write_str("WHERE ");
4572                f.write_node(expr);
4573            }
4574        }
4575    }
4576}
4577impl_display_t!(ShowStatementFilter);
4578
4579#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4580pub enum WithOptionValue<T: AstInfo> {
4581    Value(Value),
4582    DataType(T::DataType),
4583    Secret(T::ItemName),
4584    Item(T::ItemName),
4585    UnresolvedItemName(UnresolvedItemName),
4586    Ident(Ident),
4587    Sequence(Vec<WithOptionValue<T>>),
4588    Map(BTreeMap<String, WithOptionValue<T>>),
4589    // Special cases.
4590    Expr(Expr<T>),
4591    ClusterReplicas(Vec<ReplicaDefinition<T>>),
4592    ConnectionKafkaBroker(KafkaBroker<T>),
4593    ConnectionAwsPrivatelink(ConnectionDefaultAwsPrivatelink<T>),
4594    KafkaMatchingBrokerRule(KafkaMatchingBrokerRule<T>),
4595    RetainHistoryFor(Value),
4596    Refresh(RefreshOptionValue<T>),
4597    ClusterScheduleOptionValue(ClusterScheduleOptionValue),
4598    ClusterAutoScalingStrategyOptionValue(ClusterAutoScalingStrategyOptionValue),
4599    ClusterAlterStrategy(ClusterAlterOptionValue<T>),
4600    NetworkPolicyRules(Vec<NetworkPolicyRuleDefinition<T>>),
4601}
4602
4603impl<T: AstInfo> AstDisplay for WithOptionValue<T> {
4604    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4605        if f.redacted() {
4606            // When adding branches to this match statement, think about whether it is OK for us to collect
4607            // the value as part of our telemetry. Check the data management policy to be sure!
4608            match self {
4609                WithOptionValue::Value(_)
4610                | WithOptionValue::Sequence(_)
4611                | WithOptionValue::Map(_)
4612                | WithOptionValue::RetainHistoryFor(_)
4613                | WithOptionValue::Refresh(_)
4614                | WithOptionValue::Expr(_) => {
4615                    // These are redact-aware.
4616                }
4617                WithOptionValue::ConnectionKafkaBroker(_) => {
4618                    f.write_str("'<REDACTED>'");
4619                    return;
4620                }
4621                // A secret reference is a catalog item name, not the secret
4622                // value, so it is safe to show in redacted output. An option
4623                // that accepts an inline credential is parsed as a `Value`,
4624                // which is redacted by that arm together with the option's
4625                // `redact_value()`.
4626                WithOptionValue::Secret(_) => {}
4627                WithOptionValue::DataType(_)
4628                | WithOptionValue::Item(_)
4629                | WithOptionValue::UnresolvedItemName(_)
4630                | WithOptionValue::Ident(_)
4631                | WithOptionValue::ConnectionAwsPrivatelink(_)
4632                | WithOptionValue::KafkaMatchingBrokerRule(_)
4633                | WithOptionValue::ClusterReplicas(_)
4634                | WithOptionValue::ClusterScheduleOptionValue(_)
4635                | WithOptionValue::ClusterAutoScalingStrategyOptionValue(_)
4636                | WithOptionValue::ClusterAlterStrategy(_)
4637                | WithOptionValue::NetworkPolicyRules(_) => {
4638                    // These do not need redaction.
4639                }
4640            }
4641        }
4642        match self {
4643            WithOptionValue::Sequence(values) => {
4644                f.write_str("(");
4645                f.write_node(&display::comma_separated(values));
4646                f.write_str(")");
4647            }
4648            WithOptionValue::Map(values) => {
4649                f.write_str("MAP[");
4650                let len = values.len();
4651                for (i, (key, value)) in values.iter().enumerate() {
4652                    f.write_str("'");
4653                    f.write_node(&display::escape_single_quote_string(key));
4654                    f.write_str("' => ");
4655                    f.write_node(value);
4656                    if i + 1 < len {
4657                        f.write_str(", ");
4658                    }
4659                }
4660                f.write_str("]");
4661            }
4662            WithOptionValue::Expr(e) => f.write_node(e),
4663            WithOptionValue::Value(value) => f.write_node(value),
4664            WithOptionValue::DataType(typ) => f.write_node(typ),
4665            WithOptionValue::Secret(name) => {
4666                f.write_str("SECRET ");
4667                f.write_node(name)
4668            }
4669            WithOptionValue::Item(obj) => f.write_node(obj),
4670            WithOptionValue::UnresolvedItemName(r) => f.write_node(r),
4671            WithOptionValue::Ident(r) => f.write_node(r),
4672            WithOptionValue::ClusterReplicas(replicas) => {
4673                f.write_str("(");
4674                f.write_node(&display::comma_separated(replicas));
4675                f.write_str(")");
4676            }
4677            WithOptionValue::NetworkPolicyRules(rules) => {
4678                f.write_str("(");
4679                f.write_node(&display::comma_separated(rules));
4680                f.write_str(")");
4681            }
4682            WithOptionValue::ConnectionAwsPrivatelink(aws_privatelink) => {
4683                f.write_node(aws_privatelink);
4684            }
4685            WithOptionValue::KafkaMatchingBrokerRule(rule) => {
4686                f.write_node(rule);
4687            }
4688            WithOptionValue::ConnectionKafkaBroker(broker) => {
4689                f.write_node(broker);
4690            }
4691            WithOptionValue::RetainHistoryFor(value) => {
4692                f.write_str("FOR ");
4693                f.write_node(value);
4694            }
4695            WithOptionValue::Refresh(opt) => f.write_node(opt),
4696            WithOptionValue::ClusterScheduleOptionValue(value) => f.write_node(value),
4697            WithOptionValue::ClusterAutoScalingStrategyOptionValue(value) => f.write_node(value),
4698            WithOptionValue::ClusterAlterStrategy(value) => f.write_node(value),
4699        }
4700    }
4701}
4702impl_display_t!(WithOptionValue);
4703
4704#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4705pub enum RefreshOptionValue<T: AstInfo> {
4706    OnCommit,
4707    AtCreation,
4708    At(RefreshAtOptionValue<T>),
4709    Every(RefreshEveryOptionValue<T>),
4710}
4711
4712#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4713pub struct RefreshAtOptionValue<T: AstInfo> {
4714    // We need an Expr because we want to support `mz_now()`.
4715    pub time: Expr<T>,
4716}
4717
4718#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4719pub struct RefreshEveryOptionValue<T: AstInfo> {
4720    // The refresh interval.
4721    pub interval: IntervalValue,
4722    // We need an Expr because we want to support `mz_now()`.
4723    pub aligned_to: Option<Expr<T>>,
4724}
4725
4726impl<T: AstInfo> AstDisplay for RefreshOptionValue<T> {
4727    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4728        match self {
4729            RefreshOptionValue::OnCommit => {
4730                f.write_str("ON COMMIT");
4731            }
4732            RefreshOptionValue::AtCreation => {
4733                f.write_str("AT CREATION");
4734            }
4735            RefreshOptionValue::At(RefreshAtOptionValue { time }) => {
4736                f.write_str("AT ");
4737                f.write_node(time);
4738            }
4739            RefreshOptionValue::Every(RefreshEveryOptionValue {
4740                interval,
4741                aligned_to,
4742            }) => {
4743                f.write_str("EVERY '");
4744                f.write_node(interval);
4745                if let Some(aligned_to) = aligned_to {
4746                    f.write_str(" ALIGNED TO ");
4747                    f.write_node(aligned_to)
4748                }
4749            }
4750        }
4751    }
4752}
4753
4754#[derive(
4755    Debug,
4756    Clone,
4757    PartialEq,
4758    Eq,
4759    Hash,
4760    PartialOrd,
4761    Ord,
4762    Deserialize,
4763    Serialize
4764)]
4765pub enum ClusterScheduleOptionValue {
4766    Manual,
4767    Refresh {
4768        hydration_time_estimate: Option<IntervalValue>,
4769    },
4770}
4771
4772impl Default for ClusterScheduleOptionValue {
4773    fn default() -> Self {
4774        // (Has to be consistent with `impl Default for ClusterSchedule`.)
4775        ClusterScheduleOptionValue::Manual
4776    }
4777}
4778
4779impl AstDisplay for ClusterScheduleOptionValue {
4780    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4781        match self {
4782            ClusterScheduleOptionValue::Manual => {
4783                f.write_str("MANUAL");
4784            }
4785            ClusterScheduleOptionValue::Refresh {
4786                hydration_time_estimate,
4787            } => {
4788                f.write_str("ON REFRESH");
4789                if let Some(hydration_time_estimate) = hydration_time_estimate {
4790                    f.write_str(" (HYDRATION TIME ESTIMATE = '");
4791                    f.write_node(hydration_time_estimate);
4792                    f.write_str(")");
4793                }
4794            }
4795        }
4796    }
4797}
4798
4799/// The value of the `AUTO SCALING STRATEGY` cluster option: the autoscaling
4800/// policy block. Extensible: future strategies are additional optional
4801/// sub-policies, so the block grows without changing existing ones. An empty
4802/// block (all sub-policies absent) disables autoscaling for the cluster, the same
4803/// as `RESET (AUTO SCALING STRATEGY)`.
4804#[derive(
4805    Debug,
4806    Clone,
4807    PartialEq,
4808    Eq,
4809    Hash,
4810    PartialOrd,
4811    Ord,
4812    Deserialize,
4813    Serialize
4814)]
4815pub struct ClusterAutoScalingStrategyOptionValue {
4816    pub on_hydration: Option<OnHydrationOptionValue>,
4817}
4818
4819impl AstDisplay for ClusterAutoScalingStrategyOptionValue {
4820    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4821        f.write_str("(");
4822        if let Some(on_hydration) = &self.on_hydration {
4823            f.write_node(on_hydration);
4824        }
4825        f.write_str(")");
4826    }
4827}
4828
4829/// The `ON HYDRATION` autoscaling sub-policy: while objects are un-hydrated, run
4830/// an extra replica at `hydration_size` to accelerate hydration, lingering for
4831/// `linger_duration` after the steady-state replicas hydrate.
4832#[derive(
4833    Debug,
4834    Clone,
4835    PartialEq,
4836    Eq,
4837    Hash,
4838    PartialOrd,
4839    Ord,
4840    Deserialize,
4841    Serialize
4842)]
4843pub struct OnHydrationOptionValue {
4844    pub hydration_size: Value,
4845    pub linger_duration: Option<Value>,
4846}
4847
4848impl AstDisplay for OnHydrationOptionValue {
4849    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4850        f.write_str("ON HYDRATION (HYDRATION SIZE = ");
4851        f.write_node(&self.hydration_size);
4852        if let Some(linger_duration) = &self.linger_duration {
4853            f.write_str(", LINGER DURATION = ");
4854            f.write_node(linger_duration);
4855        }
4856        f.write_str(")");
4857    }
4858}
4859
4860#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4861pub enum TransactionMode {
4862    AccessMode(TransactionAccessMode),
4863    IsolationLevel(TransactionIsolationLevel),
4864}
4865
4866impl AstDisplay for TransactionMode {
4867    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4868        use TransactionMode::*;
4869        match self {
4870            AccessMode(access_mode) => f.write_node(access_mode),
4871            IsolationLevel(iso_level) => {
4872                f.write_str("ISOLATION LEVEL ");
4873                f.write_node(iso_level);
4874            }
4875        }
4876    }
4877}
4878impl_display!(TransactionMode);
4879
4880/// The access mode of a transaction, as specified by the `BEGIN ...` statement.
4881#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4882pub enum TransactionAccessMode {
4883    ReadOnly,
4884    ReadWrite,
4885}
4886
4887impl AstDisplay for TransactionAccessMode {
4888    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4889        use TransactionAccessMode::*;
4890        f.write_str(match self {
4891            ReadOnly => "READ ONLY",
4892            ReadWrite => "READ WRITE",
4893        })
4894    }
4895}
4896impl_display!(TransactionAccessMode);
4897
4898#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
4899pub enum TransactionIsolationLevel {
4900    ReadUncommitted,
4901    ReadCommitted,
4902    RepeatableRead,
4903    Serializable,
4904    StrongSessionSerializable,
4905    StrictSerializable,
4906}
4907
4908impl AstDisplay for TransactionIsolationLevel {
4909    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4910        use TransactionIsolationLevel::*;
4911        f.write_str(match self {
4912            ReadUncommitted => "READ UNCOMMITTED",
4913            ReadCommitted => "READ COMMITTED",
4914            RepeatableRead => "REPEATABLE READ",
4915            Serializable => "SERIALIZABLE",
4916            StrongSessionSerializable => "STRONG SESSION SERIALIZABLE",
4917            StrictSerializable => "STRICT SERIALIZABLE",
4918        })
4919    }
4920}
4921impl_display!(TransactionIsolationLevel);
4922
4923#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4924pub enum SetVariableTo {
4925    Default,
4926    Values(Vec<SetVariableValue>),
4927}
4928
4929impl AstDisplay for SetVariableTo {
4930    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4931        use SetVariableTo::*;
4932        match self {
4933            Values(values) => f.write_node(&display::comma_separated(values)),
4934            Default => f.write_str("DEFAULT"),
4935        }
4936    }
4937}
4938impl_display!(SetVariableTo);
4939
4940#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4941pub enum SetVariableValue {
4942    Ident(Ident),
4943    Literal(Value),
4944}
4945
4946impl AstDisplay for SetVariableValue {
4947    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4948        use SetVariableValue::*;
4949        match self {
4950            Ident(ident) => f.write_node(ident),
4951            Literal(literal) => f.write_node(literal),
4952        }
4953    }
4954}
4955impl_display!(SetVariableValue);
4956
4957impl SetVariableValue {
4958    /// Returns the underlying value without quotes.
4959    pub fn into_unquoted_value(self) -> String {
4960        match self {
4961            // `lit.to_string` will quote a `Value::String`, so get the unquoted
4962            // version.
4963            SetVariableValue::Literal(Value::String(s)) => s,
4964            SetVariableValue::Literal(lit) => lit.to_string(),
4965            SetVariableValue::Ident(ident) => ident.into_string(),
4966        }
4967    }
4968}
4969
4970/// SQL assignment `foo = expr` as used in SQLUpdate
4971#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4972pub struct Assignment<T: AstInfo> {
4973    pub id: Ident,
4974    pub value: Expr<T>,
4975}
4976
4977impl<T: AstInfo> AstDisplay for Assignment<T> {
4978    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4979        f.write_node(&self.id);
4980        f.write_str(" = ");
4981        f.write_node(&self.value);
4982    }
4983}
4984impl_display_t!(Assignment);
4985
4986/// Specifies what [Statement::ExplainPlan] is actually explained.
4987#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4988pub enum ExplainStage {
4989    /// The mz_sql::HirRelationExpr after parsing
4990    RawPlan,
4991    /// The mz_expr::MirRelationExpr after decorrelation
4992    DecorrelatedPlan,
4993    /// The mz_expr::MirRelationExpr after local optimization
4994    LocalPlan,
4995    /// The mz_expr::MirRelationExpr after global optimization
4996    GlobalPlan,
4997    /// The mz_compute_types::plan::Plan
4998    PhysicalPlan,
4999    /// The complete trace of the plan through the optimizer
5000    Trace,
5001    /// Insights about the plan
5002    PlanInsights,
5003}
5004
5005impl ExplainStage {
5006    /// Return the tracing path that corresponds to a given stage.
5007    pub fn paths(&self) -> Option<SmallVec<[NamedPlan; 4]>> {
5008        use NamedPlan::*;
5009        match self {
5010            Self::RawPlan => Some(smallvec![Raw]),
5011            Self::DecorrelatedPlan => Some(smallvec![Decorrelated]),
5012            Self::LocalPlan => Some(smallvec![Local]),
5013            Self::GlobalPlan => Some(smallvec![Global]),
5014            Self::PhysicalPlan => Some(smallvec![Physical]),
5015            Self::Trace => None,
5016            Self::PlanInsights => Some(smallvec![Raw, Global, FastPath]),
5017        }
5018    }
5019
5020    // Whether instead of the plan associated with this [`ExplainStage`] we
5021    // should show the [`NamedPlan::FastPath`] plan if available.
5022    pub fn show_fast_path(&self) -> bool {
5023        match self {
5024            Self::RawPlan => false,
5025            Self::DecorrelatedPlan => false,
5026            Self::LocalPlan => false,
5027            Self::GlobalPlan => true,
5028            Self::PhysicalPlan => true,
5029            Self::Trace => false,
5030            Self::PlanInsights => false,
5031        }
5032    }
5033}
5034
5035impl AstDisplay for ExplainStage {
5036    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5037        match self {
5038            Self::RawPlan => f.write_str("RAW PLAN"),
5039            Self::DecorrelatedPlan => f.write_str("DECORRELATED PLAN"),
5040            Self::LocalPlan => f.write_str("LOCALLY OPTIMIZED PLAN"),
5041            Self::GlobalPlan => f.write_str("OPTIMIZED PLAN"),
5042            Self::PhysicalPlan => f.write_str("PHYSICAL PLAN"),
5043            Self::Trace => f.write_str("OPTIMIZER TRACE"),
5044            Self::PlanInsights => f.write_str("PLAN INSIGHTS"),
5045        }
5046    }
5047}
5048impl_display!(ExplainStage);
5049
5050/// An enum of named plans that identifies specific stages in an optimizer trace
5051/// where these plans can be found.
5052#[derive(Clone)]
5053pub enum NamedPlan {
5054    Raw,
5055    Decorrelated,
5056    Local,
5057    Global,
5058    Physical,
5059    FastPath,
5060}
5061
5062impl NamedPlan {
5063    /// Return the [`NamedPlan`] for a given `path` if it exists.
5064    pub fn of_path(value: &str) -> Option<Self> {
5065        match value {
5066            "optimize/raw" => Some(Self::Raw),
5067            "optimize/hir_to_mir" => Some(Self::Decorrelated),
5068            "optimize/local" => Some(Self::Local),
5069            "optimize/global" => Some(Self::Global),
5070            "optimize/finalize_dataflow" => Some(Self::Physical),
5071            "optimize/fast_path" => Some(Self::FastPath),
5072            _ => None,
5073        }
5074    }
5075
5076    /// Return the tracing path under which the plan can be found in an
5077    /// optimizer trace.
5078    pub fn path(&self) -> &'static str {
5079        match self {
5080            Self::Raw => "optimize/raw",
5081            Self::Decorrelated => "optimize/hir_to_mir",
5082            Self::Local => "optimize/local",
5083            Self::Global => "optimize/global",
5084            Self::Physical => "optimize/finalize_dataflow",
5085            Self::FastPath => "optimize/fast_path",
5086        }
5087    }
5088}
5089
5090/// What is being explained.
5091/// The bools mean whether this is an EXPLAIN BROKEN.
5092#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5093pub enum Explainee<T: AstInfo> {
5094    View(T::ItemName),
5095    MaterializedView(T::ItemName),
5096    Index(T::ItemName),
5097    ReplanView(T::ItemName),
5098    ReplanMaterializedView(T::ItemName),
5099    ReplanIndex(T::ItemName),
5100    Select(Box<SelectStatement<T>>, bool),
5101    CreateView(Box<CreateViewStatement<T>>, bool),
5102    CreateMaterializedView(Box<CreateMaterializedViewStatement<T>>, bool),
5103    CreateIndex(Box<CreateIndexStatement<T>>, bool),
5104    Subscribe(Box<SubscribeStatement<T>>, bool),
5105}
5106
5107impl<T: AstInfo> Explainee<T> {
5108    pub fn name(&self) -> Option<&T::ItemName> {
5109        match self {
5110            Self::View(name)
5111            | Self::ReplanView(name)
5112            | Self::MaterializedView(name)
5113            | Self::ReplanMaterializedView(name)
5114            | Self::Index(name)
5115            | Self::ReplanIndex(name) => Some(name),
5116            Self::Select(..)
5117            | Self::CreateView(..)
5118            | Self::CreateMaterializedView(..)
5119            | Self::CreateIndex(..)
5120            | Self::Subscribe(..) => None,
5121        }
5122    }
5123
5124    pub fn is_view(&self) -> bool {
5125        use Explainee::*;
5126        matches!(self, View(_) | ReplanView(_) | CreateView(_, _))
5127    }
5128}
5129
5130impl<T: AstInfo> AstDisplay for Explainee<T> {
5131    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5132        match self {
5133            Self::View(name) => {
5134                f.write_str("VIEW ");
5135                f.write_node(name);
5136            }
5137            Self::MaterializedView(name) => {
5138                f.write_str("MATERIALIZED VIEW ");
5139                f.write_node(name);
5140            }
5141            Self::Index(name) => {
5142                f.write_str("INDEX ");
5143                f.write_node(name);
5144            }
5145            Self::ReplanView(name) => {
5146                f.write_str("REPLAN VIEW ");
5147                f.write_node(name);
5148            }
5149            Self::ReplanMaterializedView(name) => {
5150                f.write_str("REPLAN MATERIALIZED VIEW ");
5151                f.write_node(name);
5152            }
5153            Self::ReplanIndex(name) => {
5154                f.write_str("REPLAN INDEX ");
5155                f.write_node(name);
5156            }
5157            Self::Select(select, broken) => {
5158                if *broken {
5159                    f.write_str("BROKEN ");
5160                }
5161                f.write_node(select);
5162            }
5163            Self::CreateView(statement, broken) => {
5164                if *broken {
5165                    f.write_str("BROKEN ");
5166                }
5167                f.write_node(statement);
5168            }
5169            Self::CreateMaterializedView(statement, broken) => {
5170                if *broken {
5171                    f.write_str("BROKEN ");
5172                }
5173                f.write_node(statement);
5174            }
5175            Self::CreateIndex(statement, broken) => {
5176                if *broken {
5177                    f.write_str("BROKEN ");
5178                }
5179                f.write_node(statement);
5180            }
5181            Self::Subscribe(statement, broken) => {
5182                if *broken {
5183                    f.write_str("BROKEN ");
5184                }
5185                f.write_node(statement);
5186            }
5187        }
5188    }
5189}
5190impl_display_t!(Explainee);
5191
5192#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
5193pub enum ExplainFormat {
5194    /// Human readable display format
5195    Text,
5196    /// Human readable display format with full debug information
5197    VerboseText,
5198    /// Machine-consumable JSON format
5199    Json,
5200    /// Machine-consumable DOT (graphviz) format
5201    Dot,
5202}
5203
5204impl AstDisplay for ExplainFormat {
5205    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5206        match self {
5207            Self::Text => f.write_str("TEXT"),
5208            Self::VerboseText => f.write_str("VERBOSE TEXT"),
5209            Self::Json => f.write_str("JSON"),
5210            Self::Dot => f.write_str("DOT"),
5211        }
5212    }
5213}
5214impl_display!(ExplainFormat);
5215
5216#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5217pub enum IfExistsBehavior {
5218    Error,
5219    Skip,
5220    Replace,
5221}
5222
5223/// `DECLARE ...`
5224#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5225pub struct DeclareStatement<T: AstInfo> {
5226    pub name: Ident,
5227    pub stmt: Box<T::NestedStatement>,
5228    pub sql: String,
5229}
5230
5231impl<T: AstInfo> AstDisplay for DeclareStatement<T> {
5232    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5233        f.write_str("DECLARE ");
5234        f.write_node(&self.name);
5235        f.write_str(" CURSOR FOR ");
5236        f.write_node(&self.stmt);
5237    }
5238}
5239impl_display_t!(DeclareStatement);
5240
5241/// `CLOSE ...`
5242#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5243pub struct CloseStatement {
5244    pub name: Ident,
5245}
5246
5247impl AstDisplay for CloseStatement {
5248    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5249        f.write_str("CLOSE ");
5250        f.write_node(&self.name);
5251    }
5252}
5253impl_display!(CloseStatement);
5254
5255#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5256pub enum FetchOptionName {
5257    Timeout,
5258}
5259
5260impl AstDisplay for FetchOptionName {
5261    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5262        f.write_str(match self {
5263            FetchOptionName::Timeout => "TIMEOUT",
5264        })
5265    }
5266}
5267
5268impl WithOptionName for FetchOptionName {
5269    /// # WARNING
5270    ///
5271    /// Whenever implementing this trait consider very carefully whether or not
5272    /// this value could contain sensitive user data. If you're uncertain, err
5273    /// on the conservative side and return `true`.
5274    fn redact_value(&self) -> bool {
5275        match self {
5276            FetchOptionName::Timeout => false,
5277        }
5278    }
5279}
5280
5281#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5282pub struct FetchOption<T: AstInfo> {
5283    pub name: FetchOptionName,
5284    pub value: Option<WithOptionValue<T>>,
5285}
5286impl_display_for_with_option!(FetchOption);
5287
5288/// `FETCH ...`
5289#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5290pub struct FetchStatement<T: AstInfo> {
5291    pub name: Ident,
5292    pub count: Option<FetchDirection>,
5293    pub options: Vec<FetchOption<T>>,
5294}
5295
5296impl<T: AstInfo> AstDisplay for FetchStatement<T> {
5297    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5298        f.write_str("FETCH ");
5299        if let Some(ref count) = self.count {
5300            f.write_str(format!("{} ", count));
5301        }
5302        // `FETCH` consumes an optional leading `FORWARD` keyword, so a cursor
5303        // literally named `forward` printed bare with no preceding count would
5304        // be swallowed on reparse, leaving no cursor name. Force it to quote.
5305        if self.count.is_none() && self.name.as_str().eq_ignore_ascii_case("forward") {
5306            f.write_str(self.name.to_ast_string_stable());
5307        } else {
5308            f.write_node(&self.name);
5309        }
5310        if !self.options.is_empty() {
5311            f.write_str(" WITH (");
5312            f.write_node(&display::comma_separated(&self.options));
5313            f.write_str(")");
5314        }
5315    }
5316}
5317impl_display_t!(FetchStatement);
5318
5319#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5320pub enum FetchDirection {
5321    ForwardAll,
5322    ForwardCount(u64),
5323}
5324
5325impl AstDisplay for FetchDirection {
5326    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5327        match self {
5328            FetchDirection::ForwardAll => f.write_str("ALL"),
5329            FetchDirection::ForwardCount(count) => f.write_str(format!("{}", count)),
5330        }
5331    }
5332}
5333impl_display!(FetchDirection);
5334
5335/// `PREPARE ...`
5336#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5337pub struct PrepareStatement<T: AstInfo> {
5338    pub name: Ident,
5339    pub stmt: Box<T::NestedStatement>,
5340    pub sql: String,
5341}
5342
5343impl<T: AstInfo> AstDisplay for PrepareStatement<T> {
5344    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5345        f.write_str("PREPARE ");
5346        f.write_node(&self.name);
5347        f.write_str(" AS ");
5348        f.write_node(&self.stmt);
5349    }
5350}
5351impl_display_t!(PrepareStatement);
5352
5353/// `EXECUTE ...`
5354#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5355pub struct ExecuteStatement<T: AstInfo> {
5356    pub name: Ident,
5357    pub params: Vec<Expr<T>>,
5358}
5359
5360impl<T: AstInfo> AstDisplay for ExecuteStatement<T> {
5361    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5362        f.write_str("EXECUTE ");
5363        f.write_node(&self.name);
5364        if !self.params.is_empty() {
5365            f.write_str(" (");
5366            f.write_node(&display::comma_separated(&self.params));
5367            f.write_str(")");
5368        }
5369    }
5370}
5371impl_display_t!(ExecuteStatement);
5372
5373/// `EXECUTE UNIT TEST ...`
5374#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5375pub struct ExecuteUnitTestStatement<T: AstInfo> {
5376    pub name: Ident,
5377    pub target: T::ItemName,
5378    pub at_time: Option<Expr<T>>,
5379    pub mocks: Vec<MockViewDef<T>>,
5380    pub expected: ExpectedResultDef<T>,
5381}
5382
5383impl<T: AstInfo> AstDisplay for ExecuteUnitTestStatement<T> {
5384    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5385        f.write_str("EXECUTE UNIT TEST ");
5386        f.write_node(&self.name);
5387        f.write_str(" FOR ");
5388        f.write_node(&self.target);
5389        if let Some(at_time) = &self.at_time {
5390            f.write_str(" AT TIME ");
5391            f.write_node(at_time);
5392        }
5393        for (i, mock) in self.mocks.iter().enumerate() {
5394            f.write_str(if i == 0 { " MOCK " } else { ", MOCK " });
5395            f.write_node(mock);
5396        }
5397        f.write_str(" EXPECTED ");
5398        f.write_node(&self.expected);
5399    }
5400}
5401impl_display_t!(ExecuteUnitTestStatement);
5402
5403/// Mock view definition for EXECUTE UNIT TEST
5404#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5405pub struct MockViewDef<T: AstInfo> {
5406    pub name: T::ItemName,
5407    pub columns: Vec<ColumnDef<T>>,
5408    pub query: Query<T>,
5409}
5410
5411impl<T: AstInfo> AstDisplay for MockViewDef<T> {
5412    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5413        f.write_node(&self.name);
5414        f.write_str("(");
5415        f.write_node(&display::comma_separated(&self.columns));
5416        f.write_str(") AS (");
5417        f.write_node(&self.query);
5418        f.write_str(")");
5419    }
5420}
5421impl_display_t!(MockViewDef);
5422
5423/// Expected result definition for EXECUTE UNIT TEST
5424#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5425pub struct ExpectedResultDef<T: AstInfo> {
5426    pub columns: Vec<ColumnDef<T>>,
5427    pub query: Query<T>,
5428}
5429
5430impl<T: AstInfo> AstDisplay for ExpectedResultDef<T> {
5431    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5432        f.write_str("(");
5433        f.write_node(&display::comma_separated(&self.columns));
5434        f.write_str(") AS (");
5435        f.write_node(&self.query);
5436        f.write_str(")");
5437    }
5438}
5439impl_display_t!(ExpectedResultDef);
5440
5441/// `DEALLOCATE ...`
5442#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5443pub struct DeallocateStatement {
5444    pub name: Option<Ident>,
5445}
5446
5447impl AstDisplay for DeallocateStatement {
5448    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5449        f.write_str("DEALLOCATE ");
5450        match &self.name {
5451            Some(name) => f.write_node(name),
5452            None => f.write_str("ALL"),
5453        };
5454    }
5455}
5456impl_display!(DeallocateStatement);
5457
5458/// `RAISE ...`
5459#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5460pub struct RaiseStatement {
5461    pub severity: NoticeSeverity,
5462}
5463
5464impl AstDisplay for RaiseStatement {
5465    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5466        f.write_str("RAISE ");
5467        f.write_node(&self.severity);
5468    }
5469}
5470impl_display!(RaiseStatement);
5471
5472#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5473pub enum NoticeSeverity {
5474    Debug,
5475    Info,
5476    Log,
5477    Notice,
5478    Warning,
5479}
5480
5481impl AstDisplay for NoticeSeverity {
5482    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5483        f.write_str(match self {
5484            NoticeSeverity::Debug => "DEBUG",
5485            NoticeSeverity::Info => "INFO",
5486            NoticeSeverity::Log => "LOG",
5487            NoticeSeverity::Notice => "NOTICE",
5488            NoticeSeverity::Warning => "WARNING",
5489        })
5490    }
5491}
5492impl_display!(NoticeSeverity);
5493
5494/// `ALTER SYSTEM SET ...`
5495#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5496pub struct AlterSystemSetStatement {
5497    pub name: Ident,
5498    pub to: SetVariableTo,
5499}
5500
5501impl AstDisplay for AlterSystemSetStatement {
5502    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5503        f.write_str("ALTER SYSTEM SET ");
5504        f.write_node(&self.name);
5505        f.write_str(" = ");
5506        f.write_node(&self.to);
5507    }
5508}
5509impl_display!(AlterSystemSetStatement);
5510
5511/// `ALTER SYSTEM RESET ...`
5512#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5513pub struct AlterSystemResetStatement {
5514    pub name: Ident,
5515}
5516
5517impl AstDisplay for AlterSystemResetStatement {
5518    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5519        f.write_str("ALTER SYSTEM RESET ");
5520        f.write_node(&self.name);
5521    }
5522}
5523impl_display!(AlterSystemResetStatement);
5524
5525/// `ALTER SYSTEM RESET ALL`
5526#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5527pub struct AlterSystemResetAllStatement {}
5528
5529impl AstDisplay for AlterSystemResetAllStatement {
5530    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5531        f.write_str("ALTER SYSTEM RESET ALL");
5532    }
5533}
5534impl_display!(AlterSystemResetAllStatement);
5535
5536#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5537pub enum AsOf<T: AstInfo> {
5538    At(Expr<T>),
5539    AtLeast(Expr<T>),
5540}
5541
5542impl<T: AstInfo> AstDisplay for AsOf<T> {
5543    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5544        f.write_str("AS OF ");
5545        match self {
5546            AsOf::At(expr) => f.write_node(expr),
5547            AsOf::AtLeast(expr) => {
5548                f.write_str("AT LEAST ");
5549                f.write_node(expr);
5550            }
5551        }
5552    }
5553}
5554impl_display_t!(AsOf);
5555
5556#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
5557pub enum ShowStatement<T: AstInfo> {
5558    ShowObjects(ShowObjectsStatement<T>),
5559    ShowColumns(ShowColumnsStatement<T>),
5560    ShowCreateView(ShowCreateViewStatement<T>),
5561    ShowCreateMaterializedView(ShowCreateMaterializedViewStatement<T>),
5562    ShowCreateSource(ShowCreateSourceStatement<T>),
5563    ShowCreateTable(ShowCreateTableStatement<T>),
5564    ShowCreateSink(ShowCreateSinkStatement<T>),
5565    ShowCreateMetricSink(ShowCreateMetricSinkStatement<T>),
5566    ShowCreateIndex(ShowCreateIndexStatement<T>),
5567    ShowCreateConnection(ShowCreateConnectionStatement<T>),
5568    ShowCreateCluster(ShowCreateClusterStatement<T>),
5569    ShowCreateType(ShowCreateTypeStatement<T>),
5570    ShowVariable(ShowVariableStatement),
5571    InspectShard(InspectShardStatement),
5572}
5573
5574impl<T: AstInfo> AstDisplay for ShowStatement<T> {
5575    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5576        match self {
5577            ShowStatement::ShowObjects(stmt) => f.write_node(stmt),
5578            ShowStatement::ShowColumns(stmt) => f.write_node(stmt),
5579            ShowStatement::ShowCreateView(stmt) => f.write_node(stmt),
5580            ShowStatement::ShowCreateMaterializedView(stmt) => f.write_node(stmt),
5581            ShowStatement::ShowCreateSource(stmt) => f.write_node(stmt),
5582            ShowStatement::ShowCreateTable(stmt) => f.write_node(stmt),
5583            ShowStatement::ShowCreateSink(stmt) => f.write_node(stmt),
5584            ShowStatement::ShowCreateMetricSink(stmt) => f.write_node(stmt),
5585            ShowStatement::ShowCreateIndex(stmt) => f.write_node(stmt),
5586            ShowStatement::ShowCreateConnection(stmt) => f.write_node(stmt),
5587            ShowStatement::ShowCreateCluster(stmt) => f.write_node(stmt),
5588            ShowStatement::ShowCreateType(stmt) => f.write_node(stmt),
5589            ShowStatement::ShowVariable(stmt) => f.write_node(stmt),
5590            ShowStatement::InspectShard(stmt) => f.write_node(stmt),
5591        }
5592    }
5593}
5594impl_display_t!(ShowStatement);
5595
5596/// `GRANT ...`
5597#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5598pub struct GrantRoleStatement<T: AstInfo> {
5599    /// The roles that are gaining members.
5600    pub role_names: Vec<T::RoleName>,
5601    /// The roles that will be added to `role_name`.
5602    pub member_names: Vec<T::RoleName>,
5603}
5604
5605impl<T: AstInfo> AstDisplay for GrantRoleStatement<T> {
5606    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5607        f.write_str("GRANT ");
5608        f.write_node(&display::comma_separated(&self.role_names));
5609        f.write_str(" TO ");
5610        f.write_node(&display::comma_separated(&self.member_names));
5611    }
5612}
5613impl_display_t!(GrantRoleStatement);
5614
5615/// `REVOKE ...`
5616#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5617pub struct RevokeRoleStatement<T: AstInfo> {
5618    /// The roles that are losing members.
5619    pub role_names: Vec<T::RoleName>,
5620    /// The roles that will be removed from `role_name`.
5621    pub member_names: Vec<T::RoleName>,
5622}
5623
5624impl<T: AstInfo> AstDisplay for RevokeRoleStatement<T> {
5625    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5626        f.write_str("REVOKE ");
5627        f.write_node(&display::comma_separated(&self.role_names));
5628        f.write_str(" FROM ");
5629        f.write_node(&display::comma_separated(&self.member_names));
5630    }
5631}
5632impl_display_t!(RevokeRoleStatement);
5633
5634#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5635pub enum Privilege {
5636    SELECT,
5637    INSERT,
5638    UPDATE,
5639    DELETE,
5640    USAGE,
5641    CREATE,
5642    CREATEROLE,
5643    CREATEDB,
5644    CREATECLUSTER,
5645    CREATENETWORKPOLICY,
5646}
5647
5648impl AstDisplay for Privilege {
5649    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5650        f.write_str(match self {
5651            Privilege::SELECT => "SELECT",
5652            Privilege::INSERT => "INSERT",
5653            Privilege::UPDATE => "UPDATE",
5654            Privilege::DELETE => "DELETE",
5655            Privilege::CREATE => "CREATE",
5656            Privilege::USAGE => "USAGE",
5657            Privilege::CREATEROLE => "CREATEROLE",
5658            Privilege::CREATEDB => "CREATEDB",
5659            Privilege::CREATECLUSTER => "CREATECLUSTER",
5660            Privilege::CREATENETWORKPOLICY => "CREATENETWORKPOLICY",
5661        });
5662    }
5663}
5664impl_display!(Privilege);
5665
5666#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5667pub enum PrivilegeSpecification {
5668    All,
5669    Privileges(Vec<Privilege>),
5670}
5671
5672impl AstDisplay for PrivilegeSpecification {
5673    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5674        match self {
5675            PrivilegeSpecification::All => f.write_str("ALL"),
5676            PrivilegeSpecification::Privileges(privileges) => {
5677                f.write_node(&display::comma_separated(privileges))
5678            }
5679        }
5680    }
5681}
5682impl_display!(PrivilegeSpecification);
5683
5684#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5685pub enum GrantTargetSpecification<T: AstInfo> {
5686    Object {
5687        /// The type of object.
5688        ///
5689        /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5690        object_type: ObjectType,
5691        /// Specification of each object affected.
5692        object_spec_inner: GrantTargetSpecificationInner<T>,
5693    },
5694    System,
5695}
5696
5697#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5698pub enum GrantTargetSpecificationInner<T: AstInfo> {
5699    All(GrantTargetAllSpecification<T>),
5700    Objects { names: Vec<T::ObjectName> },
5701}
5702
5703#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5704pub enum GrantTargetAllSpecification<T: AstInfo> {
5705    All,
5706    AllDatabases { databases: Vec<T::DatabaseName> },
5707    AllSchemas { schemas: Vec<T::SchemaName> },
5708}
5709
5710impl<T: AstInfo> GrantTargetAllSpecification<T> {
5711    pub fn len(&self) -> usize {
5712        match self {
5713            GrantTargetAllSpecification::All => 1,
5714            GrantTargetAllSpecification::AllDatabases { databases } => databases.len(),
5715            GrantTargetAllSpecification::AllSchemas { schemas } => schemas.len(),
5716        }
5717    }
5718}
5719
5720/// Writes the plural keyword for `object_type` as `GRANT`/`REVOKE ... ON ALL`
5721/// expects it. Most object types just take a trailing `S` (`TABLES`, `SECRETS`,
5722/// ...), but `NETWORK POLICY` pluralizes to the `POLICIES` keyword the parser
5723/// accepts — naively appending `S` would emit `NETWORK POLICYS`, which fails to
5724/// reparse.
5725fn write_grant_object_type_plural<W: fmt::Write>(
5726    f: &mut AstFormatter<W>,
5727    object_type: &ObjectType,
5728) {
5729    match object_type {
5730        ObjectType::NetworkPolicy => f.write_str("POLICIES"),
5731        other => {
5732            f.write_node(other);
5733            f.write_str("S");
5734        }
5735    }
5736}
5737
5738impl<T: AstInfo> AstDisplay for GrantTargetSpecification<T> {
5739    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5740        match self {
5741            GrantTargetSpecification::Object {
5742                object_type,
5743                object_spec_inner,
5744            } => match object_spec_inner {
5745                GrantTargetSpecificationInner::All(all_spec) => match all_spec {
5746                    GrantTargetAllSpecification::All => {
5747                        f.write_str("ALL ");
5748                        write_grant_object_type_plural(f, object_type);
5749                    }
5750                    GrantTargetAllSpecification::AllDatabases { databases } => {
5751                        f.write_str("ALL ");
5752                        write_grant_object_type_plural(f, object_type);
5753                        f.write_str(" IN DATABASE ");
5754                        f.write_node(&display::comma_separated(databases));
5755                    }
5756                    GrantTargetAllSpecification::AllSchemas { schemas } => {
5757                        f.write_str("ALL ");
5758                        write_grant_object_type_plural(f, object_type);
5759                        f.write_str(" IN SCHEMA ");
5760                        f.write_node(&display::comma_separated(schemas));
5761                    }
5762                },
5763                GrantTargetSpecificationInner::Objects { names } => {
5764                    f.write_node(object_type);
5765                    f.write_str(" ");
5766                    f.write_node(&display::comma_separated(names));
5767                }
5768            },
5769            GrantTargetSpecification::System => f.write_str("SYSTEM"),
5770        }
5771    }
5772}
5773impl_display_t!(GrantTargetSpecification);
5774
5775/// `GRANT ...`
5776#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5777pub struct GrantPrivilegesStatement<T: AstInfo> {
5778    /// The privileges being granted on an object.
5779    pub privileges: PrivilegeSpecification,
5780    /// The objects that are affected by the GRANT.
5781    pub target: GrantTargetSpecification<T>,
5782    /// The roles that will granted the privileges.
5783    pub roles: Vec<T::RoleName>,
5784}
5785
5786impl<T: AstInfo> AstDisplay for GrantPrivilegesStatement<T> {
5787    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5788        f.write_str("GRANT ");
5789        f.write_node(&self.privileges);
5790        f.write_str(" ON ");
5791        f.write_node(&self.target);
5792        f.write_str(" TO ");
5793        f.write_node(&display::comma_separated(&self.roles));
5794    }
5795}
5796impl_display_t!(GrantPrivilegesStatement);
5797
5798/// `REVOKE ...`
5799#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5800pub struct RevokePrivilegesStatement<T: AstInfo> {
5801    /// The privileges being revoked.
5802    pub privileges: PrivilegeSpecification,
5803    /// The objects that are affected by the REVOKE.
5804    pub target: GrantTargetSpecification<T>,
5805    /// The roles that will have privileges revoked.
5806    pub roles: Vec<T::RoleName>,
5807}
5808
5809impl<T: AstInfo> AstDisplay for RevokePrivilegesStatement<T> {
5810    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5811        f.write_str("REVOKE ");
5812        f.write_node(&self.privileges);
5813        f.write_str(" ON ");
5814        f.write_node(&self.target);
5815        f.write_str(" FROM ");
5816        f.write_node(&display::comma_separated(&self.roles));
5817    }
5818}
5819impl_display_t!(RevokePrivilegesStatement);
5820
5821#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5822pub enum TargetRoleSpecification<T: AstInfo> {
5823    /// Specific list of roles.
5824    Roles(Vec<T::RoleName>),
5825    /// All current and future roles.
5826    AllRoles,
5827}
5828
5829impl<T: AstInfo> AstDisplay for TargetRoleSpecification<T> {
5830    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5831        match self {
5832            TargetRoleSpecification::Roles(roles) => f.write_node(&display::comma_separated(roles)),
5833            TargetRoleSpecification::AllRoles => f.write_str("ALL ROLES"),
5834        }
5835    }
5836}
5837impl_display_t!(TargetRoleSpecification);
5838
5839#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5840pub struct AbbreviatedGrantStatement<T: AstInfo> {
5841    /// The privileges being granted.
5842    pub privileges: PrivilegeSpecification,
5843    /// The type of object.
5844    ///
5845    /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5846    pub object_type: ObjectType,
5847    /// The roles that will granted the privileges.
5848    pub grantees: Vec<T::RoleName>,
5849}
5850
5851impl<T: AstInfo> AstDisplay for AbbreviatedGrantStatement<T> {
5852    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5853        f.write_str("GRANT ");
5854        f.write_node(&self.privileges);
5855        f.write_str(" ON ");
5856        write_grant_object_type_plural(f, &self.object_type);
5857        f.write_str(" TO ");
5858        f.write_node(&display::comma_separated(&self.grantees));
5859    }
5860}
5861impl_display_t!(AbbreviatedGrantStatement);
5862
5863#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5864pub struct AbbreviatedRevokeStatement<T: AstInfo> {
5865    /// The privileges being revoked.
5866    pub privileges: PrivilegeSpecification,
5867    /// The type of object.
5868    ///
5869    /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5870    pub object_type: ObjectType,
5871    /// The roles that the privilege will be revoked from.
5872    pub revokees: Vec<T::RoleName>,
5873}
5874
5875impl<T: AstInfo> AstDisplay for AbbreviatedRevokeStatement<T> {
5876    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5877        f.write_str("REVOKE ");
5878        f.write_node(&self.privileges);
5879        f.write_str(" ON ");
5880        write_grant_object_type_plural(f, &self.object_type);
5881        f.write_str(" FROM ");
5882        f.write_node(&display::comma_separated(&self.revokees));
5883    }
5884}
5885impl_display_t!(AbbreviatedRevokeStatement);
5886
5887#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5888pub enum AbbreviatedGrantOrRevokeStatement<T: AstInfo> {
5889    Grant(AbbreviatedGrantStatement<T>),
5890    Revoke(AbbreviatedRevokeStatement<T>),
5891}
5892
5893impl<T: AstInfo> AbbreviatedGrantOrRevokeStatement<T> {
5894    pub fn privileges(&self) -> &PrivilegeSpecification {
5895        match self {
5896            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.privileges,
5897            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.privileges,
5898        }
5899    }
5900
5901    pub fn object_type(&self) -> &ObjectType {
5902        match self {
5903            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.object_type,
5904            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.object_type,
5905        }
5906    }
5907
5908    pub fn roles(&self) -> &Vec<T::RoleName> {
5909        match self {
5910            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.grantees,
5911            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.revokees,
5912        }
5913    }
5914}
5915
5916impl<T: AstInfo> AstDisplay for AbbreviatedGrantOrRevokeStatement<T> {
5917    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5918        match self {
5919            AbbreviatedGrantOrRevokeStatement::Grant(grant) => f.write_node(grant),
5920            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => f.write_node(revoke),
5921        }
5922    }
5923}
5924impl_display_t!(AbbreviatedGrantOrRevokeStatement);
5925
5926/// `ALTER DEFAULT PRIVILEGES ...`
5927#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5928pub struct AlterDefaultPrivilegesStatement<T: AstInfo> {
5929    /// The roles for which created objects are affected.
5930    pub target_roles: TargetRoleSpecification<T>,
5931    /// The objects that are affected by the default privilege.
5932    pub target_objects: GrantTargetAllSpecification<T>,
5933    /// The privilege to grant or revoke.
5934    pub grant_or_revoke: AbbreviatedGrantOrRevokeStatement<T>,
5935}
5936
5937impl<T: AstInfo> AstDisplay for AlterDefaultPrivilegesStatement<T> {
5938    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5939        f.write_str("ALTER DEFAULT PRIVILEGES");
5940        match &self.target_roles {
5941            TargetRoleSpecification::Roles(_) => {
5942                f.write_str(" FOR ROLE ");
5943                f.write_node(&self.target_roles);
5944            }
5945            TargetRoleSpecification::AllRoles => {
5946                f.write_str(" FOR ");
5947                f.write_node(&self.target_roles);
5948            }
5949        }
5950        match &self.target_objects {
5951            GrantTargetAllSpecification::All => {}
5952            GrantTargetAllSpecification::AllDatabases { databases } => {
5953                f.write_str(" IN DATABASE ");
5954                f.write_node(&display::comma_separated(databases));
5955            }
5956            GrantTargetAllSpecification::AllSchemas { schemas } => {
5957                f.write_str(" IN SCHEMA ");
5958                f.write_node(&display::comma_separated(schemas));
5959            }
5960        }
5961        f.write_str(" ");
5962        f.write_node(&self.grant_or_revoke);
5963    }
5964}
5965impl_display_t!(AlterDefaultPrivilegesStatement);
5966
5967/// `REASSIGN OWNED ...`
5968#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5969pub struct ReassignOwnedStatement<T: AstInfo> {
5970    /// The roles whose owned objects are being reassigned.
5971    pub old_roles: Vec<T::RoleName>,
5972    /// The new owner of the objects.
5973    pub new_role: T::RoleName,
5974}
5975
5976impl<T: AstInfo> AstDisplay for ReassignOwnedStatement<T> {
5977    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5978        f.write_str("REASSIGN OWNED BY ");
5979        f.write_node(&display::comma_separated(&self.old_roles));
5980        f.write_str(" TO ");
5981        f.write_node(&self.new_role);
5982    }
5983}
5984impl_display_t!(ReassignOwnedStatement);
5985
5986/// `COMMENT ON ...`
5987#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5988pub struct CommentStatement<T: AstInfo> {
5989    pub object: CommentObjectType<T>,
5990    pub comment: Option<String>,
5991}
5992
5993impl<T: AstInfo> AstDisplay for CommentStatement<T> {
5994    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5995        f.write_str("COMMENT ON ");
5996        f.write_node(&self.object);
5997
5998        f.write_str(" IS ");
5999        match &self.comment {
6000            Some(s) => {
6001                if f.redacted() {
6002                    // The comment body is arbitrary free text and may contain PII,
6003                    // so redact it like every other user-supplied value.
6004                    f.write_str("'<REDACTED>'");
6005                } else {
6006                    f.write_str("'");
6007                    f.write_node(&display::escape_single_quote_string(s));
6008                    f.write_str("'");
6009                }
6010            }
6011            None => f.write_str("NULL"),
6012        }
6013    }
6014}
6015impl_display_t!(CommentStatement);
6016
6017#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
6018pub struct ColumnName<T: AstInfo> {
6019    pub relation: T::ItemName,
6020    pub column: T::ColumnReference,
6021}
6022
6023impl<T: AstInfo> AstDisplay for ColumnName<T> {
6024    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
6025        f.write_node(&self.relation);
6026        f.write_str(".");
6027        f.write_node(&self.column);
6028    }
6029}
6030impl_display_t!(ColumnName);
6031
6032#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6033pub enum CommentObjectType<T: AstInfo> {
6034    Table { name: T::ItemName },
6035    View { name: T::ItemName },
6036    Column { name: ColumnName<T> },
6037    MaterializedView { name: T::ItemName },
6038    Source { name: T::ItemName },
6039    Sink { name: T::ItemName },
6040    Index { name: T::ItemName },
6041    Func { name: T::ItemName },
6042    Connection { name: T::ItemName },
6043    Type { ty: T::DataType },
6044    Secret { name: T::ItemName },
6045    Role { name: T::RoleName },
6046    Database { name: T::DatabaseName },
6047    Schema { name: T::SchemaName },
6048    Cluster { name: T::ClusterName },
6049    ClusterReplica { name: QualifiedReplica },
6050    NetworkPolicy { name: T::NetworkPolicyName },
6051}
6052
6053impl<T: AstInfo> AstDisplay for CommentObjectType<T> {
6054    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
6055        use CommentObjectType::*;
6056
6057        match self {
6058            Table { name } => {
6059                f.write_str("TABLE ");
6060                f.write_node(name);
6061            }
6062            View { name } => {
6063                f.write_str("VIEW ");
6064                f.write_node(name);
6065            }
6066            Column { name } => {
6067                f.write_str("COLUMN ");
6068                f.write_node(name);
6069            }
6070            MaterializedView { name } => {
6071                f.write_str("MATERIALIZED VIEW ");
6072                f.write_node(name);
6073            }
6074            Source { name } => {
6075                f.write_str("SOURCE ");
6076                f.write_node(name);
6077            }
6078            Sink { name } => {
6079                f.write_str("SINK ");
6080                f.write_node(name);
6081            }
6082            Index { name } => {
6083                f.write_str("INDEX ");
6084                f.write_node(name);
6085            }
6086            Func { name } => {
6087                f.write_str("FUNCTION ");
6088                f.write_node(name);
6089            }
6090            Connection { name } => {
6091                f.write_str("CONNECTION ");
6092                f.write_node(name);
6093            }
6094            Type { ty } => {
6095                f.write_str("TYPE ");
6096                f.write_node(ty);
6097            }
6098            Secret { name } => {
6099                f.write_str("SECRET ");
6100                f.write_node(name);
6101            }
6102            Role { name } => {
6103                f.write_str("ROLE ");
6104                f.write_node(name);
6105            }
6106            Database { name } => {
6107                f.write_str("DATABASE ");
6108                f.write_node(name);
6109            }
6110            Schema { name } => {
6111                f.write_str("SCHEMA ");
6112                f.write_node(name);
6113            }
6114            Cluster { name } => {
6115                f.write_str("CLUSTER ");
6116                f.write_node(name);
6117            }
6118            ClusterReplica { name } => {
6119                f.write_str("CLUSTER REPLICA ");
6120                f.write_node(name);
6121            }
6122            NetworkPolicy { name } => {
6123                f.write_str("NETWORK POLICY ");
6124                f.write_node(name);
6125            }
6126        }
6127    }
6128}
6129
6130impl_display_t!(CommentObjectType);
6131
6132// Include the `AstDisplay` implementations for simple options derived by the
6133// crate's build.rs script.
6134include!(concat!(env!("OUT_DIR"), "/display.simple_options.rs"));