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    /// Hex-encoded protobuf of a `ProtoSourceExportStatementDetails`
1785    /// message, which includes details necessary for planning this
1786    /// table as a Source Export
1787    Details,
1788    /// Partition the given table by the provided columns.
1789    PartitionBy,
1790    // The `RETAIN HISTORY` option
1791    RetainHistory,
1792}
1793
1794impl AstDisplay for TableFromSourceOptionName {
1795    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1796        f.write_str(match self {
1797            TableFromSourceOptionName::TextColumns => "TEXT COLUMNS",
1798            TableFromSourceOptionName::ExcludeColumns => "EXCLUDE COLUMNS",
1799            TableFromSourceOptionName::ExcludeConstraints => "EXCLUDE CONSTRAINTS",
1800            TableFromSourceOptionName::ExcludeAllConstraints => "EXCLUDE ALL CONSTRAINTS",
1801            TableFromSourceOptionName::Details => "DETAILS",
1802            TableFromSourceOptionName::PartitionBy => "PARTITION BY",
1803            TableFromSourceOptionName::RetainHistory => "RETAIN HISTORY",
1804        })
1805    }
1806}
1807impl_display!(TableFromSourceOptionName);
1808
1809impl WithOptionName for TableFromSourceOptionName {
1810    /// # WARNING
1811    ///
1812    /// Whenever implementing this trait consider very carefully whether or not
1813    /// this value could contain sensitive user data. If you're uncertain, err
1814    /// on the conservative side and return `true`.
1815    fn redact_value(&self) -> bool {
1816        match self {
1817            TableFromSourceOptionName::Details
1818            | TableFromSourceOptionName::TextColumns
1819            | TableFromSourceOptionName::ExcludeColumns
1820            | TableFromSourceOptionName::ExcludeConstraints
1821            | TableFromSourceOptionName::ExcludeAllConstraints
1822            | TableFromSourceOptionName::RetainHistory => false,
1823            // The value is an arbitrary user expression/literal that may embed
1824            // sensitive data, so redact it (mirrors `KafkaSinkConfigOptionName`).
1825            TableFromSourceOptionName::PartitionBy => true,
1826        }
1827    }
1828}
1829
1830#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1831pub struct TableFromSourceOption<T: AstInfo> {
1832    pub name: TableFromSourceOptionName,
1833    pub value: Option<WithOptionValue<T>>,
1834}
1835impl_display_for_with_option!(TableFromSourceOption);
1836
1837/// `CREATE TABLE .. FROM SOURCE` columns specification
1838/// can have 3 states:
1839/// Before purification they can be `NotSpecified` or `Named`
1840/// by the user to specify the column names to use.
1841/// After purification they can be in any of the 3 states.
1842/// For some source types we define the columns during purification
1843/// and for others the columns are defined during planning based
1844/// on the encoding option of the source.
1845#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1846pub enum TableFromSourceColumns<T: AstInfo> {
1847    /// The user did not specify which columns to use.
1848    NotSpecified,
1849    /// The user requested the named columns. Only compatible
1850    /// with source types that allow user-specified column names.
1851    Named(Vec<Ident>),
1852    /// Columns defined during purification for some source types.
1853    Defined(Vec<ColumnDef<T>>),
1854}
1855
1856/// `CREATE TABLE .. FROM SOURCE`
1857#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1858pub struct CreateTableFromSourceStatement<T: AstInfo> {
1859    pub name: UnresolvedItemName,
1860    pub columns: TableFromSourceColumns<T>,
1861    pub constraints: Vec<TableConstraint<T>>,
1862    pub if_not_exists: bool,
1863    pub source: T::ItemName,
1864    pub external_reference: Option<UnresolvedItemName>,
1865    pub with_options: Vec<TableFromSourceOption<T>>,
1866    pub include_metadata: Vec<SourceIncludeMetadata>,
1867    pub format: Option<FormatSpecifier<T>>,
1868    pub envelope: Option<SourceEnvelope>,
1869}
1870
1871impl<T: AstInfo> AstDisplay for CreateTableFromSourceStatement<T> {
1872    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1873        let Self {
1874            name,
1875            columns,
1876            constraints,
1877            source,
1878            external_reference,
1879            if_not_exists,
1880            with_options,
1881            include_metadata,
1882            format,
1883            envelope,
1884        } = self;
1885        f.write_str("CREATE TABLE ");
1886        if *if_not_exists {
1887            f.write_str("IF NOT EXISTS ");
1888        }
1889        f.write_node(name);
1890        if !matches!(columns, TableFromSourceColumns::NotSpecified) || !constraints.is_empty() {
1891            f.write_str(" (");
1892
1893            match columns {
1894                TableFromSourceColumns::NotSpecified => {}
1895                TableFromSourceColumns::Named(columns) => {
1896                    f.write_node(&display::comma_separated(columns))
1897                }
1898                TableFromSourceColumns::Defined(columns) => {
1899                    f.write_node(&display::comma_separated(columns))
1900                }
1901            };
1902            if !constraints.is_empty() {
1903                if !matches!(columns, TableFromSourceColumns::NotSpecified) {
1904                    f.write_str(", ");
1905                }
1906                f.write_node(&display::comma_separated(constraints));
1907            }
1908            f.write_str(")");
1909        }
1910        f.write_str(" FROM SOURCE ");
1911        f.write_node(source);
1912        if let Some(external_reference) = external_reference {
1913            f.write_str(" (REFERENCE = ");
1914            f.write_node(external_reference);
1915            f.write_str(")");
1916        }
1917
1918        if let Some(format) = &format {
1919            f.write_str(" ");
1920            f.write_node(format);
1921        }
1922        if !include_metadata.is_empty() {
1923            f.write_str(" INCLUDE ");
1924            f.write_node(&display::comma_separated(include_metadata));
1925        }
1926        if let Some(envelope) = &envelope {
1927            f.write_str(" ENVELOPE ");
1928            f.write_node(envelope);
1929        }
1930        if !with_options.is_empty() {
1931            f.write_str(" WITH (");
1932            f.write_node(&display::comma_separated(with_options));
1933            f.write_str(")");
1934        }
1935    }
1936}
1937impl_display_t!(CreateTableFromSourceStatement);
1938
1939/// `CREATE INDEX`
1940#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1941pub struct CreateIndexStatement<T: AstInfo> {
1942    /// Optional index name.
1943    pub name: Option<Ident>,
1944    pub in_cluster: Option<T::ClusterName>,
1945    /// `ON` table or view name
1946    pub on_name: T::ItemName,
1947    /// Expressions that form part of the index key. If not included, the
1948    /// key_parts will be inferred from the named object.
1949    pub key_parts: Option<Vec<Expr<T>>>,
1950    pub with_options: Vec<IndexOption<T>>,
1951    pub if_not_exists: bool,
1952}
1953
1954impl<T: AstInfo> AstDisplay for CreateIndexStatement<T> {
1955    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
1956        f.write_str("CREATE ");
1957        if self.key_parts.is_none() {
1958            f.write_str("DEFAULT ");
1959        }
1960        f.write_str("INDEX ");
1961        if self.if_not_exists {
1962            f.write_str("IF NOT EXISTS ");
1963        }
1964        if let Some(name) = &self.name {
1965            // A bare `in` index name re-lexes as the start of the optional
1966            // `IN CLUSTER` clause below (`CREATE INDEX in ON …` fails to reparse
1967            // with "Expected ON, found IN"), so force it quoted. `in` is
1968            // legitimately bare in other name positions — e.g. a required
1969            // `CREATE SINK` name — so this is local to the optional-name +
1970            // `IN CLUSTER` ambiguity, not a `can_be_printed_bare` case.
1971            if name.as_str().eq_ignore_ascii_case("in") {
1972                f.write_str("\"");
1973                f.write_str(name.as_str());
1974                f.write_str("\"");
1975            } else {
1976                f.write_node(name);
1977            }
1978            f.write_str(" ");
1979        }
1980        if let Some(cluster) = &self.in_cluster {
1981            f.write_str("IN CLUSTER ");
1982            f.write_node(cluster);
1983            f.write_str(" ");
1984        }
1985        f.write_str("ON ");
1986        f.write_node(&self.on_name);
1987        if let Some(key_parts) = &self.key_parts {
1988            f.write_str(" (");
1989            f.write_node(&display::comma_separated(key_parts));
1990            f.write_str(")");
1991        }
1992        if !self.with_options.is_empty() {
1993            f.write_str(" WITH (");
1994            f.write_node(&display::comma_separated(&self.with_options));
1995            f.write_str(")");
1996        }
1997    }
1998}
1999impl_display_t!(CreateIndexStatement);
2000
2001/// An option in a `CREATE CLUSTER` statement.
2002#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2003pub enum IndexOptionName {
2004    // The `RETAIN HISTORY` option
2005    RetainHistory,
2006}
2007
2008impl AstDisplay for IndexOptionName {
2009    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2010        match self {
2011            IndexOptionName::RetainHistory => {
2012                f.write_str("RETAIN HISTORY");
2013            }
2014        }
2015    }
2016}
2017
2018impl WithOptionName for IndexOptionName {
2019    /// # WARNING
2020    ///
2021    /// Whenever implementing this trait consider very carefully whether or not
2022    /// this value could contain sensitive user data. If you're uncertain, err
2023    /// on the conservative side and return `true`.
2024    fn redact_value(&self) -> bool {
2025        match self {
2026            IndexOptionName::RetainHistory => false,
2027        }
2028    }
2029}
2030
2031#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2032pub struct IndexOption<T: AstInfo> {
2033    pub name: IndexOptionName,
2034    pub value: Option<WithOptionValue<T>>,
2035}
2036impl_display_for_with_option!(IndexOption);
2037
2038/// A `CREATE ROLE` statement.
2039#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2040pub struct CreateRoleStatement {
2041    /// The specified role.
2042    pub name: Ident,
2043    /// Any options that were attached, in the order they were presented.
2044    pub options: Vec<RoleAttribute>,
2045}
2046
2047impl AstDisplay for CreateRoleStatement {
2048    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2049        f.write_str("CREATE ");
2050        f.write_str("ROLE ");
2051        f.write_node(&self.name);
2052        for option in &self.options {
2053            f.write_str(" ");
2054            option.fmt(f)
2055        }
2056    }
2057}
2058impl_display!(CreateRoleStatement);
2059
2060/// Attributes that can be attached to roles.
2061#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2062pub enum RoleAttribute {
2063    /// The `INHERIT` option.
2064    Inherit,
2065    /// The `NOINHERIT` option.
2066    NoInherit,
2067    /// The `PASSWORD` option.
2068    Password(Option<String>),
2069    // The following are not supported, but included to give helpful error messages.
2070    Login,
2071    NoLogin,
2072    SuperUser,
2073    NoSuperUser,
2074    CreateCluster,
2075    NoCreateCluster,
2076    CreateDB,
2077    NoCreateDB,
2078    CreateRole,
2079    NoCreateRole,
2080}
2081
2082impl AstDisplay for RoleAttribute {
2083    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2084        match self {
2085            RoleAttribute::SuperUser => f.write_str("SUPERUSER"),
2086            RoleAttribute::NoSuperUser => f.write_str("NOSUPERUSER"),
2087            RoleAttribute::Login => f.write_str("LOGIN"),
2088            RoleAttribute::NoLogin => f.write_str("NOLOGIN"),
2089            RoleAttribute::Inherit => f.write_str("INHERIT"),
2090            RoleAttribute::NoInherit => f.write_str("NOINHERIT"),
2091            RoleAttribute::CreateCluster => f.write_str("CREATECLUSTER"),
2092            RoleAttribute::NoCreateCluster => f.write_str("NOCREATECLUSTER"),
2093            RoleAttribute::CreateDB => f.write_str("CREATEDB"),
2094            RoleAttribute::NoCreateDB => f.write_str("NOCREATEDB"),
2095            RoleAttribute::CreateRole => f.write_str("CREATEROLE"),
2096            RoleAttribute::NoCreateRole => f.write_str("NOCREATEROLE"),
2097            // `PASSWORD NULL` removes the password and carries no secret, so
2098            // print it verbatim. A `PASSWORD '<secret>'` is always redacted (in
2099            // every mode, matching the prior behavior) — but as a *parseable*
2100            // placeholder string, not a bare `PASSWORD`, which fails to reparse
2101            // (the grammar requires `NULL` or a string literal after `PASSWORD`).
2102            RoleAttribute::Password(None) => f.write_str("PASSWORD NULL"),
2103            RoleAttribute::Password(Some(_)) => f.write_str("PASSWORD '<REDACTED>'"),
2104        }
2105    }
2106}
2107impl_display!(RoleAttribute);
2108
2109/// `ALTER ROLE role_name [SET | RESET] ...`
2110#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2111pub enum SetRoleVar {
2112    /// `SET name TO value`
2113    Set { name: Ident, value: SetVariableTo },
2114    /// `RESET name`
2115    Reset { name: Ident },
2116}
2117
2118impl AstDisplay for SetRoleVar {
2119    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2120        match self {
2121            SetRoleVar::Set { name, value } => {
2122                f.write_str("SET ");
2123                f.write_node(name);
2124                f.write_str(" = ");
2125                f.write_node(value);
2126            }
2127            SetRoleVar::Reset { name } => {
2128                f.write_str("RESET ");
2129                f.write_node(name);
2130            }
2131        }
2132    }
2133}
2134impl_display!(SetRoleVar);
2135
2136/// AN `ALTER NETWORK POLICY` statement.
2137#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2138pub struct AlterNetworkPolicyStatement<T: AstInfo> {
2139    /// The specified Network Policy.
2140    pub name: Ident,
2141    /// Any options that were attached, in the order they were presented.
2142    pub options: Vec<NetworkPolicyOption<T>>,
2143}
2144
2145impl<T: AstInfo> AstDisplay for AlterNetworkPolicyStatement<T> {
2146    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2147        f.write_str("ALTER ");
2148        f.write_str("NETWORK POLICY ");
2149        f.write_node(&self.name);
2150        f.write_str(" SET (");
2151        f.write_node(&display::comma_separated(&self.options));
2152        f.write_str(" )");
2153    }
2154}
2155impl_display_t!(AlterNetworkPolicyStatement);
2156
2157/// A `CREATE NETWORK POLICY` statement.
2158#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2159pub struct CreateNetworkPolicyStatement<T: AstInfo> {
2160    /// The specified network policy.
2161    pub name: Ident,
2162    /// Any options that were attached, in the order they were presented.
2163    pub options: Vec<NetworkPolicyOption<T>>,
2164}
2165
2166impl<T: AstInfo> AstDisplay for CreateNetworkPolicyStatement<T> {
2167    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2168        f.write_str("CREATE ");
2169        f.write_str("NETWORK POLICY ");
2170        f.write_node(&self.name);
2171        f.write_str(" (");
2172        f.write_node(&display::comma_separated(&self.options));
2173        f.write_str(" )");
2174    }
2175}
2176impl_display_t!(CreateNetworkPolicyStatement);
2177
2178#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2179pub struct NetworkPolicyOption<T: AstInfo> {
2180    pub name: NetworkPolicyOptionName,
2181    pub value: Option<WithOptionValue<T>>,
2182}
2183
2184#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2185pub enum NetworkPolicyOptionName {
2186    Rules,
2187}
2188
2189impl WithOptionName for NetworkPolicyOptionName {
2190    /// # WARNING
2191    ///
2192    /// Whenever implementing this trait consider very carefully whether or not
2193    /// this value could contain sensitive user data. If you're uncertain, err
2194    /// on the conservative side and return `true`.
2195    fn redact_value(&self) -> bool {
2196        match self {
2197            NetworkPolicyOptionName::Rules => false,
2198        }
2199    }
2200}
2201
2202impl AstDisplay for NetworkPolicyOptionName {
2203    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2204        match self {
2205            NetworkPolicyOptionName::Rules => f.write_str("RULES"),
2206        }
2207    }
2208}
2209impl_display_for_with_option!(NetworkPolicyOption);
2210
2211#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2212pub struct NetworkPolicyRuleDefinition<T: AstInfo> {
2213    pub name: Ident,
2214    pub options: Vec<NetworkPolicyRuleOption<T>>,
2215}
2216
2217impl<T: AstInfo> AstDisplay for NetworkPolicyRuleDefinition<T> {
2218    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2219        f.write_node(&self.name);
2220        f.write_str(" (");
2221        f.write_node(&display::comma_separated(&self.options));
2222        f.write_str(" )");
2223    }
2224}
2225
2226#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2227pub struct NetworkPolicyRuleOption<T: AstInfo> {
2228    pub name: NetworkPolicyRuleOptionName,
2229    pub value: Option<WithOptionValue<T>>,
2230}
2231
2232#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2233pub enum NetworkPolicyRuleOptionName {
2234    Direction,
2235    Action,
2236    Address,
2237}
2238
2239impl WithOptionName for NetworkPolicyRuleOptionName {
2240    /// # WARNING
2241    ///
2242    /// Whenever implementing this trait consider very carefully whether or not
2243    /// this value could contain sensitive user data. If you're uncertain, err
2244    /// on the conservative side and return `true`.
2245    fn redact_value(&self) -> bool {
2246        match self {
2247            NetworkPolicyRuleOptionName::Direction
2248            | NetworkPolicyRuleOptionName::Action
2249            | NetworkPolicyRuleOptionName::Address => false,
2250        }
2251    }
2252}
2253
2254impl AstDisplay for NetworkPolicyRuleOptionName {
2255    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2256        match self {
2257            NetworkPolicyRuleOptionName::Direction => f.write_str("DIRECTION"),
2258            NetworkPolicyRuleOptionName::Action => f.write_str("ACTION"),
2259            NetworkPolicyRuleOptionName::Address => f.write_str("ADDRESS"),
2260        }
2261    }
2262}
2263
2264impl_display_for_with_option!(NetworkPolicyRuleOption);
2265
2266/// A `CREATE SECRET` statement.
2267#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2268pub struct CreateSecretStatement<T: AstInfo> {
2269    pub name: UnresolvedItemName,
2270    pub if_not_exists: bool,
2271    pub value: Expr<T>,
2272}
2273
2274impl<T: AstInfo> AstDisplay for CreateSecretStatement<T> {
2275    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2276        f.write_str("CREATE SECRET ");
2277        if self.if_not_exists {
2278            f.write_str("IF NOT EXISTS ");
2279        }
2280        f.write_node(&self.name);
2281        f.write_str(" AS ");
2282
2283        if f.redacted() {
2284            f.write_str("'<REDACTED>'");
2285        } else {
2286            f.write_node(&self.value);
2287        }
2288    }
2289}
2290impl_display_t!(CreateSecretStatement);
2291
2292/// `CREATE TYPE ..`
2293#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2294pub struct CreateTypeStatement<T: AstInfo> {
2295    /// Name of the created type.
2296    pub name: UnresolvedItemName,
2297    /// The new type's "base type".
2298    pub as_type: CreateTypeAs<T>,
2299}
2300
2301impl<T: AstInfo> AstDisplay for CreateTypeStatement<T> {
2302    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2303        f.write_str("CREATE TYPE ");
2304        f.write_node(&self.name);
2305        f.write_str(" AS ");
2306        match &self.as_type {
2307            CreateTypeAs::List { options } => {
2308                f.write_str(&self.as_type);
2309                f.write_str("(");
2310                if !options.is_empty() {
2311                    f.write_node(&display::comma_separated(options));
2312                }
2313                f.write_str(")");
2314            }
2315            CreateTypeAs::Map { options } => {
2316                f.write_str(&self.as_type);
2317                f.write_str("(");
2318                if !options.is_empty() {
2319                    f.write_node(&display::comma_separated(options));
2320                }
2321                f.write_str(")");
2322            }
2323            CreateTypeAs::Record { column_defs } => {
2324                f.write_str("(");
2325                if !column_defs.is_empty() {
2326                    f.write_node(&display::comma_separated(column_defs));
2327                }
2328                f.write_str(")");
2329            }
2330        };
2331    }
2332}
2333impl_display_t!(CreateTypeStatement);
2334
2335#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2336pub enum ClusterOptionName {
2337    /// The `AUTO SCALING STRATEGY [[=] (...)]` option.
2338    AutoScalingStrategy,
2339    /// The `AVAILABILITY ZONES [[=] '[' <values> ']' ]` option.
2340    AvailabilityZones,
2341    /// The `DISK` option.
2342    Disk,
2343    /// The `EXPERIMENTAL ARRANGEMENT COMPRESSION [[=] <enabled>]` option.
2344    ExperimentalArrangementCompression,
2345    /// The `INTROSPECTION INTERVAL [[=] <interval>]` option.
2346    IntrospectionInterval,
2347    /// The `INTROSPECTION DEBUGGING [[=] <enabled>]` option.
2348    IntrospectionDebugging,
2349    /// The `MANAGED` option.
2350    Managed,
2351    /// The `REPLICAS` option.
2352    Replicas,
2353    /// The `REPLICATION FACTOR` option.
2354    ReplicationFactor,
2355    /// The `SIZE` option.
2356    Size,
2357    /// The `SCHEDULE` option.
2358    Schedule,
2359    /// The `WORKLOAD CLASS` option.
2360    WorkloadClass,
2361}
2362
2363impl AstDisplay for ClusterOptionName {
2364    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2365        match self {
2366            ClusterOptionName::AutoScalingStrategy => f.write_str("AUTO SCALING STRATEGY"),
2367            ClusterOptionName::AvailabilityZones => f.write_str("AVAILABILITY ZONES"),
2368            ClusterOptionName::Disk => f.write_str("DISK"),
2369            ClusterOptionName::ExperimentalArrangementCompression => {
2370                f.write_str("EXPERIMENTAL ARRANGEMENT COMPRESSION")
2371            }
2372            ClusterOptionName::IntrospectionDebugging => f.write_str("INTROSPECTION DEBUGGING"),
2373            ClusterOptionName::IntrospectionInterval => f.write_str("INTROSPECTION INTERVAL"),
2374            ClusterOptionName::Managed => f.write_str("MANAGED"),
2375            ClusterOptionName::Replicas => f.write_str("REPLICAS"),
2376            ClusterOptionName::ReplicationFactor => f.write_str("REPLICATION FACTOR"),
2377            ClusterOptionName::Size => f.write_str("SIZE"),
2378            ClusterOptionName::Schedule => f.write_str("SCHEDULE"),
2379            ClusterOptionName::WorkloadClass => f.write_str("WORKLOAD CLASS"),
2380        }
2381    }
2382}
2383
2384impl WithOptionName for ClusterOptionName {
2385    /// # WARNING
2386    ///
2387    /// Whenever implementing this trait consider very carefully whether or not
2388    /// this value could contain sensitive user data. If you're uncertain, err
2389    /// on the conservative side and return `true`.
2390    fn redact_value(&self) -> bool {
2391        match self {
2392            ClusterOptionName::AutoScalingStrategy
2393            | ClusterOptionName::AvailabilityZones
2394            | ClusterOptionName::Disk
2395            | ClusterOptionName::ExperimentalArrangementCompression
2396            | ClusterOptionName::IntrospectionDebugging
2397            | ClusterOptionName::IntrospectionInterval
2398            | ClusterOptionName::Managed
2399            | ClusterOptionName::Replicas
2400            | ClusterOptionName::ReplicationFactor
2401            | ClusterOptionName::Size
2402            | ClusterOptionName::Schedule
2403            | ClusterOptionName::WorkloadClass => false,
2404        }
2405    }
2406}
2407
2408#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2409/// An option in a `CREATE CLUSTER` statement.
2410pub struct ClusterOption<T: AstInfo> {
2411    pub name: ClusterOptionName,
2412    pub value: Option<WithOptionValue<T>>,
2413}
2414impl_display_for_with_option!(ClusterOption);
2415
2416#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2417pub enum ClusterAlterUntilReadyOptionName {
2418    Timeout,
2419    OnTimeout,
2420}
2421
2422impl AstDisplay for ClusterAlterUntilReadyOptionName {
2423    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2424        match self {
2425            Self::Timeout => f.write_str("TIMEOUT"),
2426            Self::OnTimeout => f.write_str("ON TIMEOUT"),
2427        }
2428    }
2429}
2430
2431impl WithOptionName for ClusterAlterUntilReadyOptionName {
2432    /// # WARNING
2433    ///
2434    /// Whenever implementing this trait consider very carefully whether or not
2435    /// this value could contain sensitive user data. If you're uncertain, err
2436    /// on the conservative side and return `true`.
2437    fn redact_value(&self) -> bool {
2438        match self {
2439            ClusterAlterUntilReadyOptionName::Timeout
2440            | ClusterAlterUntilReadyOptionName::OnTimeout => false,
2441        }
2442    }
2443}
2444
2445#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2446pub struct ClusterAlterUntilReadyOption<T: AstInfo> {
2447    pub name: ClusterAlterUntilReadyOptionName,
2448    pub value: Option<WithOptionValue<T>>,
2449}
2450impl_display_for_with_option!(ClusterAlterUntilReadyOption);
2451
2452#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2453pub enum ClusterAlterOptionName {
2454    Wait,
2455}
2456
2457impl AstDisplay for ClusterAlterOptionName {
2458    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2459        match self {
2460            ClusterAlterOptionName::Wait => f.write_str("WAIT"),
2461        }
2462    }
2463}
2464
2465impl WithOptionName for ClusterAlterOptionName {
2466    /// # WARNING
2467    ///
2468    /// Whenever implementing this trait consider very carefully whether or not
2469    /// this value could contain sensitive user data. If you're uncertain, err
2470    /// on the conservative side and return `true`.
2471    fn redact_value(&self) -> bool {
2472        match self {
2473            ClusterAlterOptionName::Wait => false,
2474        }
2475    }
2476}
2477
2478#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2479pub enum ClusterAlterOptionValue<T: AstInfo> {
2480    For(Value),
2481    UntilReady(Vec<ClusterAlterUntilReadyOption<T>>),
2482}
2483
2484impl<T: AstInfo> AstDisplay for ClusterAlterOptionValue<T> {
2485    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2486        match self {
2487            ClusterAlterOptionValue::For(duration) => {
2488                f.write_str("FOR ");
2489                f.write_node(duration);
2490            }
2491            ClusterAlterOptionValue::UntilReady(options) => {
2492                f.write_str("UNTIL READY (");
2493                f.write_node(&display::comma_separated(options));
2494                f.write_str(")");
2495            }
2496        }
2497    }
2498}
2499
2500#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2501/// An option in a `ALTER CLUSTER... WITH` statement.
2502pub struct ClusterAlterOption<T: AstInfo> {
2503    pub name: ClusterAlterOptionName,
2504    pub value: Option<WithOptionValue<T>>,
2505}
2506
2507impl_display_for_with_option!(ClusterAlterOption);
2508
2509// Note: the `AstDisplay` implementation and `Parser::parse_` method for this
2510// enum are generated automatically by this crate's `build.rs`.
2511#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2512pub enum ClusterFeatureName {
2513    ReoptimizeImportedViews,
2514    EnableNewOuterJoinLowering,
2515    EnableEagerDeltaJoins,
2516    EnableVariadicLeftJoinLowering,
2517    EnableLetrecFixpointAnalysis,
2518    EnableJoinPrioritizeArranged,
2519    EnableProjectionPushdownAfterRelationCse,
2520    EnableUnionCancellationAfterRelationCse,
2521}
2522
2523impl WithOptionName for ClusterFeatureName {
2524    /// # WARNING
2525    ///
2526    /// Whenever implementing this trait consider very carefully whether or not
2527    /// this value could contain sensitive user data. If you're uncertain, err
2528    /// on the conservative side and return `true`.
2529    fn redact_value(&self) -> bool {
2530        match self {
2531            Self::ReoptimizeImportedViews
2532            | Self::EnableNewOuterJoinLowering
2533            | Self::EnableEagerDeltaJoins
2534            | Self::EnableVariadicLeftJoinLowering
2535            | Self::EnableLetrecFixpointAnalysis
2536            | Self::EnableJoinPrioritizeArranged
2537            | Self::EnableProjectionPushdownAfterRelationCse
2538            | Self::EnableUnionCancellationAfterRelationCse => false,
2539        }
2540    }
2541}
2542
2543#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2544pub struct ClusterFeature<T: AstInfo> {
2545    pub name: ClusterFeatureName,
2546    pub value: Option<WithOptionValue<T>>,
2547}
2548impl_display_for_with_option!(ClusterFeature);
2549
2550/// `CREATE CLUSTER ..`
2551#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2552pub struct CreateClusterStatement<T: AstInfo> {
2553    /// Name of the created cluster.
2554    pub name: Ident,
2555    /// The comma-separated options.
2556    pub options: Vec<ClusterOption<T>>,
2557    /// The comma-separated features enabled on the cluster.
2558    pub features: Vec<ClusterFeature<T>>,
2559    pub if_not_exists: bool,
2560}
2561
2562impl<T: AstInfo> AstDisplay for CreateClusterStatement<T> {
2563    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2564        f.write_str("CREATE CLUSTER ");
2565        if self.if_not_exists {
2566            f.write_str("IF NOT EXISTS ");
2567        }
2568        f.write_node(&self.name);
2569        if !self.options.is_empty() {
2570            f.write_str(" (");
2571            f.write_node(&display::comma_separated(&self.options));
2572            f.write_str(")");
2573        }
2574        if !self.features.is_empty() {
2575            f.write_str(" FEATURES (");
2576            f.write_node(&display::comma_separated(&self.features));
2577            f.write_str(")");
2578        }
2579    }
2580}
2581impl_display_t!(CreateClusterStatement);
2582
2583#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2584pub struct ReplicaDefinition<T: AstInfo> {
2585    /// Name of the created replica.
2586    pub name: Ident,
2587    /// The comma-separated options.
2588    pub options: Vec<ReplicaOption<T>>,
2589}
2590
2591// Note that this display is meant for replicas defined inline when creating
2592// clusters.
2593impl<T: AstInfo> AstDisplay for ReplicaDefinition<T> {
2594    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2595        f.write_node(&self.name);
2596        f.write_str(" (");
2597        f.write_node(&display::comma_separated(&self.options));
2598        f.write_str(")");
2599    }
2600}
2601impl_display_t!(ReplicaDefinition);
2602
2603#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2604pub enum AlterClusterAction<T: AstInfo> {
2605    SetOptions {
2606        options: Vec<ClusterOption<T>>,
2607        with_options: Vec<ClusterAlterOption<T>>,
2608    },
2609    ResetOptions(Vec<ClusterOptionName>),
2610}
2611
2612/// `ALTER CLUSTER .. SET ...`
2613#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2614pub struct AlterClusterStatement<T: AstInfo> {
2615    /// The `IF EXISTS` option.
2616    pub if_exists: bool,
2617    /// Name of the altered cluster.
2618    pub name: Ident,
2619    /// The action.
2620    pub action: AlterClusterAction<T>,
2621}
2622
2623impl<T: AstInfo> AstDisplay for AlterClusterStatement<T> {
2624    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2625        f.write_str("ALTER CLUSTER ");
2626        if self.if_exists {
2627            f.write_str("IF EXISTS ");
2628        }
2629        f.write_node(&self.name);
2630        f.write_str(" ");
2631        match &self.action {
2632            AlterClusterAction::SetOptions {
2633                options,
2634                with_options,
2635            } => {
2636                f.write_str("SET (");
2637                f.write_node(&display::comma_separated(options));
2638                f.write_str(")");
2639                if !with_options.is_empty() {
2640                    f.write_str(" WITH (");
2641                    f.write_node(&display::comma_separated(with_options));
2642                    f.write_str(")");
2643                }
2644            }
2645            AlterClusterAction::ResetOptions(options) => {
2646                f.write_str("RESET (");
2647                f.write_node(&display::comma_separated(options));
2648                f.write_str(")");
2649            }
2650        }
2651    }
2652}
2653impl_display_t!(AlterClusterStatement);
2654
2655/// `CREATE CLUSTER REPLICA ..`
2656#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2657pub struct CreateClusterReplicaStatement<T: AstInfo> {
2658    /// Name of the replica's cluster.
2659    pub of_cluster: Ident,
2660    /// The replica's definition.
2661    pub definition: ReplicaDefinition<T>,
2662    pub if_not_exists: bool,
2663}
2664
2665impl<T: AstInfo> AstDisplay for CreateClusterReplicaStatement<T> {
2666    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2667        f.write_str("CREATE CLUSTER REPLICA ");
2668        if self.if_not_exists {
2669            f.write_str("IF NOT EXISTS ");
2670        }
2671        f.write_node(&self.of_cluster);
2672        f.write_str(".");
2673        f.write_node(&self.definition.name);
2674        f.write_str(" (");
2675        f.write_node(&display::comma_separated(&self.definition.options));
2676        f.write_str(")");
2677    }
2678}
2679impl_display_t!(CreateClusterReplicaStatement);
2680
2681#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2682pub enum ReplicaOptionName {
2683    /// The `BILLED AS [=] <value>` option.
2684    BilledAs,
2685    /// The `SIZE [[=] <size>]` option.
2686    Size,
2687    /// The `AVAILABILITY ZONE [[=] <id>]` option.
2688    AvailabilityZone,
2689    /// The `STORAGE ADDRESSES` option.
2690    StorageAddresses,
2691    /// The `STORAGECTL ADDRESSES` option.
2692    StoragectlAddresses,
2693    /// The `COMPUTECTL ADDRESSES` option.
2694    ComputectlAddresses,
2695    /// The `COMPUTE ADDRESSES` option.
2696    ComputeAddresses,
2697    /// The `WORKERS` option.
2698    Workers,
2699    /// The `INTERNAL` option.
2700    Internal,
2701    /// The `INTROSPECTION INTERVAL [[=] <interval>]` option.
2702    IntrospectionInterval,
2703    /// The `INTROSPECTION DEBUGGING [[=] <enabled>]` option.
2704    IntrospectionDebugging,
2705    /// The `DISK [[=] <enabled>]` option.
2706    Disk,
2707    /// The `EXPERIMENTAL ARRANGEMENT COMPRESSION [[=] <enabled>]` option.
2708    ExperimentalArrangementCompression,
2709}
2710
2711impl AstDisplay for ReplicaOptionName {
2712    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2713        match self {
2714            ReplicaOptionName::BilledAs => f.write_str("BILLED AS"),
2715            ReplicaOptionName::Size => f.write_str("SIZE"),
2716            ReplicaOptionName::AvailabilityZone => f.write_str("AVAILABILITY ZONE"),
2717            ReplicaOptionName::StorageAddresses => f.write_str("STORAGE ADDRESSES"),
2718            ReplicaOptionName::StoragectlAddresses => f.write_str("STORAGECTL ADDRESSES"),
2719            ReplicaOptionName::ComputectlAddresses => f.write_str("COMPUTECTL ADDRESSES"),
2720            ReplicaOptionName::ComputeAddresses => f.write_str("COMPUTE ADDRESSES"),
2721            ReplicaOptionName::Workers => f.write_str("WORKERS"),
2722            ReplicaOptionName::Internal => f.write_str("INTERNAL"),
2723            ReplicaOptionName::IntrospectionInterval => f.write_str("INTROSPECTION INTERVAL"),
2724            ReplicaOptionName::IntrospectionDebugging => f.write_str("INTROSPECTION DEBUGGING"),
2725            ReplicaOptionName::Disk => f.write_str("DISK"),
2726            ReplicaOptionName::ExperimentalArrangementCompression => {
2727                f.write_str("EXPERIMENTAL ARRANGEMENT COMPRESSION")
2728            }
2729        }
2730    }
2731}
2732
2733impl WithOptionName for ReplicaOptionName {
2734    /// # WARNING
2735    ///
2736    /// Whenever implementing this trait consider very carefully whether or not
2737    /// this value could contain sensitive user data. If you're uncertain, err
2738    /// on the conservative side and return `true`.
2739    fn redact_value(&self) -> bool {
2740        match self {
2741            ReplicaOptionName::BilledAs
2742            | ReplicaOptionName::Size
2743            | ReplicaOptionName::AvailabilityZone
2744            | ReplicaOptionName::StorageAddresses
2745            | ReplicaOptionName::StoragectlAddresses
2746            | ReplicaOptionName::ComputectlAddresses
2747            | ReplicaOptionName::ComputeAddresses
2748            | ReplicaOptionName::Workers
2749            | ReplicaOptionName::Internal
2750            | ReplicaOptionName::IntrospectionInterval
2751            | ReplicaOptionName::IntrospectionDebugging
2752            | ReplicaOptionName::Disk
2753            | ReplicaOptionName::ExperimentalArrangementCompression => false,
2754        }
2755    }
2756}
2757
2758#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2759/// An option in a `CREATE CLUSTER REPLICA` statement.
2760pub struct ReplicaOption<T: AstInfo> {
2761    pub name: ReplicaOptionName,
2762    pub value: Option<WithOptionValue<T>>,
2763}
2764impl_display_for_with_option!(ReplicaOption);
2765
2766/// `CREATE TYPE .. AS <TYPE>`
2767#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2768pub enum CreateTypeAs<T: AstInfo> {
2769    List {
2770        options: Vec<CreateTypeListOption<T>>,
2771    },
2772    Map {
2773        options: Vec<CreateTypeMapOption<T>>,
2774    },
2775    Record {
2776        column_defs: Vec<ColumnDef<T>>,
2777    },
2778}
2779
2780impl<T: AstInfo> AstDisplay for CreateTypeAs<T> {
2781    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2782        match self {
2783            CreateTypeAs::List { .. } => f.write_str("LIST "),
2784            CreateTypeAs::Map { .. } => f.write_str("MAP "),
2785            CreateTypeAs::Record { .. } => f.write_str("RECORD "),
2786        }
2787    }
2788}
2789impl_display_t!(CreateTypeAs);
2790
2791#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2792pub enum CreateTypeListOptionName {
2793    ElementType,
2794}
2795
2796impl AstDisplay for CreateTypeListOptionName {
2797    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2798        f.write_str(match self {
2799            CreateTypeListOptionName::ElementType => "ELEMENT TYPE",
2800        })
2801    }
2802}
2803
2804impl WithOptionName for CreateTypeListOptionName {
2805    /// # WARNING
2806    ///
2807    /// Whenever implementing this trait consider very carefully whether or not
2808    /// this value could contain sensitive user data. If you're uncertain, err
2809    /// on the conservative side and return `true`.
2810    fn redact_value(&self) -> bool {
2811        match self {
2812            CreateTypeListOptionName::ElementType => false,
2813        }
2814    }
2815}
2816
2817#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2818pub struct CreateTypeListOption<T: AstInfo> {
2819    pub name: CreateTypeListOptionName,
2820    pub value: Option<WithOptionValue<T>>,
2821}
2822impl_display_for_with_option!(CreateTypeListOption);
2823
2824#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2825pub enum CreateTypeMapOptionName {
2826    KeyType,
2827    ValueType,
2828}
2829
2830impl AstDisplay for CreateTypeMapOptionName {
2831    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2832        f.write_str(match self {
2833            CreateTypeMapOptionName::KeyType => "KEY TYPE",
2834            CreateTypeMapOptionName::ValueType => "VALUE TYPE",
2835        })
2836    }
2837}
2838
2839impl WithOptionName for CreateTypeMapOptionName {
2840    /// # WARNING
2841    ///
2842    /// Whenever implementing this trait consider very carefully whether or not
2843    /// this value could contain sensitive user data. If you're uncertain, err
2844    /// on the conservative side and return `true`.
2845    fn redact_value(&self) -> bool {
2846        match self {
2847            CreateTypeMapOptionName::KeyType | CreateTypeMapOptionName::ValueType => false,
2848        }
2849    }
2850}
2851
2852#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2853pub struct CreateTypeMapOption<T: AstInfo> {
2854    pub name: CreateTypeMapOptionName,
2855    pub value: Option<WithOptionValue<T>>,
2856}
2857impl_display_for_with_option!(CreateTypeMapOption);
2858
2859/// `ALTER <OBJECT> ... OWNER TO`
2860#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2861pub struct AlterOwnerStatement<T: AstInfo> {
2862    pub object_type: ObjectType,
2863    pub if_exists: bool,
2864    pub name: UnresolvedObjectName,
2865    pub new_owner: T::RoleName,
2866}
2867
2868impl<T: AstInfo> AstDisplay for AlterOwnerStatement<T> {
2869    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2870        f.write_str("ALTER ");
2871        f.write_node(&self.object_type);
2872        f.write_str(" ");
2873        if self.if_exists {
2874            f.write_str("IF EXISTS ");
2875        }
2876        f.write_node(&self.name);
2877        f.write_str(" OWNER TO ");
2878        f.write_node(&self.new_owner);
2879    }
2880}
2881impl_display_t!(AlterOwnerStatement);
2882
2883/// `ALTER <OBJECT> ... RENAME TO`
2884#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2885pub struct AlterObjectRenameStatement {
2886    pub object_type: ObjectType,
2887    pub if_exists: bool,
2888    pub name: UnresolvedObjectName,
2889    pub to_item_name: Ident,
2890}
2891
2892impl AstDisplay for AlterObjectRenameStatement {
2893    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2894        f.write_str("ALTER ");
2895        f.write_node(&self.object_type);
2896        f.write_str(" ");
2897        if self.if_exists {
2898            f.write_str("IF EXISTS ");
2899        }
2900        f.write_node(&self.name);
2901        f.write_str(" RENAME TO ");
2902        f.write_node(&self.to_item_name);
2903    }
2904}
2905impl_display!(AlterObjectRenameStatement);
2906
2907/// `ALTER <OBJECT> ... [RE]SET (RETAIN HISTORY [FOR ...])`
2908#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2909pub struct AlterRetainHistoryStatement<T: AstInfo> {
2910    pub object_type: ObjectType,
2911    pub if_exists: bool,
2912    pub name: UnresolvedObjectName,
2913    pub history: Option<WithOptionValue<T>>,
2914}
2915
2916impl<T: AstInfo> AstDisplay for AlterRetainHistoryStatement<T> {
2917    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2918        f.write_str("ALTER ");
2919        f.write_node(&self.object_type);
2920        f.write_str(" ");
2921        if self.if_exists {
2922            f.write_str("IF EXISTS ");
2923        }
2924        f.write_node(&self.name);
2925        if let Some(history) = &self.history {
2926            f.write_str(" SET (RETAIN HISTORY ");
2927            f.write_node(history);
2928        } else {
2929            f.write_str(" RESET (RETAIN HISTORY");
2930        }
2931        f.write_str(")");
2932    }
2933}
2934impl_display_t!(AlterRetainHistoryStatement);
2935
2936/// `ALTER <OBJECT> SWAP ...`
2937#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2938pub struct AlterObjectSwapStatement {
2939    pub object_type: ObjectType,
2940    pub if_exists: bool,
2941    pub name_a: UnresolvedObjectName,
2942    pub name_b: Ident,
2943}
2944
2945impl AstDisplay for AlterObjectSwapStatement {
2946    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2947        f.write_str("ALTER ");
2948
2949        f.write_node(&self.object_type);
2950        f.write_str(" ");
2951        if self.if_exists {
2952            f.write_str("IF EXISTS ");
2953        }
2954        f.write_node(&self.name_a);
2955
2956        f.write_str(" SWAP WITH ");
2957        f.write_node(&self.name_b);
2958    }
2959}
2960impl_display!(AlterObjectSwapStatement);
2961
2962#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2963pub enum AlterIndexAction<T: AstInfo> {
2964    SetOptions(Vec<IndexOption<T>>),
2965    ResetOptions(Vec<IndexOptionName>),
2966}
2967
2968/// `ALTER INDEX ... {RESET, SET}`
2969#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2970pub struct AlterIndexStatement<T: AstInfo> {
2971    pub index_name: UnresolvedItemName,
2972    pub if_exists: bool,
2973    pub action: AlterIndexAction<T>,
2974}
2975
2976impl<T: AstInfo> AstDisplay for AlterIndexStatement<T> {
2977    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2978        f.write_str("ALTER INDEX ");
2979        if self.if_exists {
2980            f.write_str("IF EXISTS ");
2981        }
2982        f.write_node(&self.index_name);
2983        f.write_str(" ");
2984
2985        match &self.action {
2986            AlterIndexAction::SetOptions(options) => {
2987                f.write_str("SET (");
2988                f.write_node(&display::comma_separated(options));
2989                f.write_str(")");
2990            }
2991            AlterIndexAction::ResetOptions(options) => {
2992                f.write_str("RESET (");
2993                f.write_node(&display::comma_separated(options));
2994                f.write_str(")");
2995            }
2996        }
2997    }
2998}
2999
3000impl_display_t!(AlterIndexStatement);
3001
3002#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3003pub enum AlterSinkAction<T: AstInfo> {
3004    SetOptions(Vec<CreateSinkOption<T>>),
3005    ResetOptions(Vec<CreateSinkOptionName>),
3006    ChangeRelation(T::ItemName),
3007}
3008
3009#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3010pub struct AlterSinkStatement<T: AstInfo> {
3011    pub sink_name: UnresolvedItemName,
3012    pub if_exists: bool,
3013    pub action: AlterSinkAction<T>,
3014}
3015
3016impl<T: AstInfo> AstDisplay for AlterSinkStatement<T> {
3017    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3018        f.write_str("ALTER SINK ");
3019        if self.if_exists {
3020            f.write_str("IF EXISTS ");
3021        }
3022        f.write_node(&self.sink_name);
3023        f.write_str(" ");
3024
3025        match &self.action {
3026            AlterSinkAction::ChangeRelation(from) => {
3027                f.write_str("SET FROM ");
3028                f.write_node(from);
3029            }
3030            AlterSinkAction::SetOptions(options) => {
3031                f.write_str("SET (");
3032                f.write_node(&display::comma_separated(options));
3033                f.write_str(")");
3034            }
3035            AlterSinkAction::ResetOptions(options) => {
3036                f.write_str("RESET (");
3037                f.write_node(&display::comma_separated(options));
3038                f.write_str(")");
3039            }
3040        }
3041    }
3042}
3043
3044#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3045pub enum AlterSourceAddSubsourceOptionName {
3046    /// Columns whose types you want to unconditionally format as text
3047    TextColumns,
3048    /// Columns you want to ignore when ingesting data
3049    ExcludeColumns,
3050    /// Updated `DETAILS` for an ingestion, e.g.
3051    /// [`crate::ast::PgConfigOptionName::Details`]
3052    /// or
3053    /// [`crate::ast::MySqlConfigOptionName::Details`].
3054    Details,
3055}
3056
3057impl AstDisplay for AlterSourceAddSubsourceOptionName {
3058    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3059        f.write_str(match self {
3060            AlterSourceAddSubsourceOptionName::TextColumns => "TEXT COLUMNS",
3061            AlterSourceAddSubsourceOptionName::ExcludeColumns => "EXCLUDE COLUMNS",
3062            AlterSourceAddSubsourceOptionName::Details => "DETAILS",
3063        })
3064    }
3065}
3066impl_display!(AlterSourceAddSubsourceOptionName);
3067
3068impl WithOptionName for AlterSourceAddSubsourceOptionName {
3069    /// # WARNING
3070    ///
3071    /// Whenever implementing this trait consider very carefully whether or not
3072    /// this value could contain sensitive user data. If you're uncertain, err
3073    /// on the conservative side and return `true`.
3074    fn redact_value(&self) -> bool {
3075        match self {
3076            AlterSourceAddSubsourceOptionName::Details
3077            | AlterSourceAddSubsourceOptionName::TextColumns
3078            | AlterSourceAddSubsourceOptionName::ExcludeColumns => false,
3079        }
3080    }
3081}
3082
3083#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3084/// An option in an `ALTER SOURCE...ADD SUBSOURCE` statement.
3085pub struct AlterSourceAddSubsourceOption<T: AstInfo> {
3086    pub name: AlterSourceAddSubsourceOptionName,
3087    pub value: Option<WithOptionValue<T>>,
3088}
3089impl_display_for_with_option!(AlterSourceAddSubsourceOption);
3090impl_display_t!(AlterSourceAddSubsourceOption);
3091
3092#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3093pub enum AlterSourceAction<T: AstInfo> {
3094    SetOptions(Vec<CreateSourceOption<T>>),
3095    ResetOptions(Vec<CreateSourceOptionName>),
3096    AddSubsources {
3097        external_references: Vec<ExternalReferenceExport>,
3098        options: Vec<AlterSourceAddSubsourceOption<T>>,
3099    },
3100    DropSubsources {
3101        if_exists: bool,
3102        cascade: bool,
3103        names: Vec<UnresolvedItemName>,
3104    },
3105    RefreshReferences,
3106}
3107
3108impl<T: AstInfo> AstDisplay for AlterSourceAction<T> {
3109    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3110    where
3111        W: fmt::Write,
3112    {
3113        match &self {
3114            AlterSourceAction::SetOptions(options) => {
3115                f.write_str("SET (");
3116                f.write_node(&display::comma_separated(options));
3117                f.write_str(")");
3118            }
3119            AlterSourceAction::ResetOptions(options) => {
3120                f.write_str("RESET (");
3121                f.write_node(&display::comma_separated(options));
3122                f.write_str(")");
3123            }
3124            AlterSourceAction::DropSubsources {
3125                if_exists,
3126                cascade,
3127                names,
3128            } => {
3129                f.write_str("DROP SUBSOURCE ");
3130                if *if_exists {
3131                    f.write_str("IF EXISTS ");
3132                }
3133
3134                f.write_node(&display::comma_separated(names));
3135
3136                if *cascade {
3137                    f.write_str(" CASCADE");
3138                }
3139            }
3140            AlterSourceAction::AddSubsources {
3141                external_references: subsources,
3142                options,
3143            } => {
3144                f.write_str("ADD SUBSOURCE ");
3145
3146                f.write_node(&display::comma_separated(subsources));
3147
3148                if !options.is_empty() {
3149                    f.write_str(" WITH (");
3150                    f.write_node(&display::comma_separated(options));
3151                    f.write_str(")");
3152                }
3153            }
3154            AlterSourceAction::RefreshReferences => {
3155                f.write_str("REFRESH REFERENCES");
3156            }
3157        }
3158    }
3159}
3160
3161#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3162pub struct AlterSourceStatement<T: AstInfo> {
3163    pub source_name: UnresolvedItemName,
3164    pub if_exists: bool,
3165    pub action: AlterSourceAction<T>,
3166}
3167
3168impl<T: AstInfo> AstDisplay for AlterSourceStatement<T> {
3169    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3170        f.write_str("ALTER SOURCE ");
3171        if self.if_exists {
3172            f.write_str("IF EXISTS ");
3173        }
3174        f.write_node(&self.source_name);
3175        f.write_str(" ");
3176        f.write_node(&self.action)
3177    }
3178}
3179
3180impl_display_t!(AlterSourceStatement);
3181
3182/// `ALTER SECRET ... AS`
3183#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3184pub struct AlterSecretStatement<T: AstInfo> {
3185    pub name: UnresolvedItemName,
3186    pub if_exists: bool,
3187    pub value: Expr<T>,
3188}
3189
3190impl<T: AstInfo> AstDisplay for AlterSecretStatement<T> {
3191    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3192        f.write_str("ALTER SECRET ");
3193        if self.if_exists {
3194            f.write_str("IF EXISTS ");
3195        }
3196        f.write_node(&self.name);
3197        f.write_str(" AS ");
3198
3199        if f.redacted() {
3200            f.write_str("'<REDACTED>'");
3201        } else {
3202            f.write_node(&self.value);
3203        }
3204    }
3205}
3206
3207impl_display_t!(AlterSecretStatement);
3208
3209#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3210pub enum AlterConnectionAction<T: AstInfo> {
3211    RotateKeys,
3212    SetOption(ConnectionOption<T>),
3213    DropOption(ConnectionOptionName),
3214}
3215
3216impl<T: AstInfo> AstDisplay for AlterConnectionAction<T> {
3217    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3218        match self {
3219            AlterConnectionAction::RotateKeys => f.write_str("ROTATE KEYS"),
3220            AlterConnectionAction::SetOption(option) => {
3221                f.write_str("SET (");
3222                f.write_node(option);
3223                f.write_str(")");
3224            }
3225            AlterConnectionAction::DropOption(option) => {
3226                f.write_str("DROP (");
3227                f.write_node(option);
3228                f.write_str(")");
3229            }
3230        }
3231    }
3232}
3233impl_display_t!(AlterConnectionAction);
3234
3235#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3236pub enum AlterConnectionOptionName {
3237    Validate,
3238}
3239
3240impl AstDisplay for AlterConnectionOptionName {
3241    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3242        f.write_str(match self {
3243            AlterConnectionOptionName::Validate => "VALIDATE",
3244        })
3245    }
3246}
3247impl_display!(AlterConnectionOptionName);
3248
3249impl WithOptionName for AlterConnectionOptionName {
3250    /// # WARNING
3251    ///
3252    /// Whenever implementing this trait consider very carefully whether or not
3253    /// this value could contain sensitive user data. If you're uncertain, err
3254    /// on the conservative side and return `true`.
3255    fn redact_value(&self) -> bool {
3256        match self {
3257            AlterConnectionOptionName::Validate => false,
3258        }
3259    }
3260}
3261
3262#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3263/// An option in an `ALTER CONNECTION...` statement.
3264pub struct AlterConnectionOption<T: AstInfo> {
3265    pub name: AlterConnectionOptionName,
3266    pub value: Option<WithOptionValue<T>>,
3267}
3268impl_display_for_with_option!(AlterConnectionOption);
3269impl_display_t!(AlterConnectionOption);
3270
3271/// `ALTER CONNECTION`
3272#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3273pub struct AlterConnectionStatement<T: AstInfo> {
3274    pub name: UnresolvedItemName,
3275    pub if_exists: bool,
3276    pub actions: Vec<AlterConnectionAction<T>>,
3277    pub with_options: Vec<AlterConnectionOption<T>>,
3278}
3279
3280impl<T: AstInfo> AstDisplay for AlterConnectionStatement<T> {
3281    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3282        f.write_str("ALTER CONNECTION ");
3283        if self.if_exists {
3284            f.write_str("IF EXISTS ");
3285        }
3286        f.write_node(&self.name);
3287        f.write_str(" ");
3288        f.write_node(&display::comma_separated(&self.actions));
3289
3290        if !self.with_options.is_empty() {
3291            f.write_str(" WITH (");
3292            f.write_node(&display::comma_separated(&self.with_options));
3293            f.write_str(")");
3294        }
3295    }
3296}
3297
3298impl_display_t!(AlterConnectionStatement);
3299
3300/// `ALTER ROLE`
3301#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3302pub struct AlterRoleStatement<T: AstInfo> {
3303    /// The specified role.
3304    pub name: T::RoleName,
3305    /// Alterations we're making to the role.
3306    pub option: AlterRoleOption,
3307}
3308
3309impl<T: AstInfo> AstDisplay for AlterRoleStatement<T> {
3310    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3311        f.write_str("ALTER ROLE ");
3312        f.write_node(&self.name);
3313        f.write_node(&self.option);
3314    }
3315}
3316impl_display_t!(AlterRoleStatement);
3317
3318/// `ALTER ROLE ... [ WITH | SET ] ...`
3319#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3320pub enum AlterRoleOption {
3321    /// Any options that were attached, in the order they were presented.
3322    Attributes(Vec<RoleAttribute>),
3323    /// A variable that we want to provide a default value for this role.
3324    Variable(SetRoleVar),
3325}
3326
3327impl AstDisplay for AlterRoleOption {
3328    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3329        match self {
3330            AlterRoleOption::Attributes(attrs) => {
3331                for attr in attrs {
3332                    f.write_str(" ");
3333                    attr.fmt(f)
3334                }
3335            }
3336            AlterRoleOption::Variable(var) => {
3337                f.write_str(" ");
3338                f.write_node(var);
3339            }
3340        }
3341    }
3342}
3343impl_display!(AlterRoleOption);
3344
3345/// `ALTER TABLE ... ADD COLUMN ...`
3346#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3347pub struct AlterTableAddColumnStatement<T: AstInfo> {
3348    pub if_exists: bool,
3349    pub name: UnresolvedItemName,
3350    pub if_col_not_exist: bool,
3351    pub column_name: Ident,
3352    pub data_type: T::DataType,
3353}
3354
3355impl<T: AstInfo> AstDisplay for AlterTableAddColumnStatement<T> {
3356    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3357    where
3358        W: fmt::Write,
3359    {
3360        f.write_str("ALTER TABLE ");
3361        if self.if_exists {
3362            f.write_str("IF EXISTS ");
3363        }
3364        f.write_node(&self.name);
3365
3366        f.write_str(" ADD COLUMN ");
3367        if self.if_col_not_exist {
3368            f.write_str("IF NOT EXISTS ");
3369        }
3370
3371        f.write_node(&self.column_name);
3372        f.write_str(" ");
3373        f.write_node(&self.data_type);
3374    }
3375}
3376
3377impl_display_t!(AlterTableAddColumnStatement);
3378
3379/// `ALTER MATERIALIZED VIEW ... APPLY REPLACEMENT ...`
3380#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3381pub struct AlterMaterializedViewApplyReplacementStatement {
3382    pub if_exists: bool,
3383    pub name: UnresolvedItemName,
3384    pub replacement_name: UnresolvedItemName,
3385}
3386
3387impl AstDisplay for AlterMaterializedViewApplyReplacementStatement {
3388    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3389    where
3390        W: fmt::Write,
3391    {
3392        f.write_str("ALTER MATERIALIZED VIEW ");
3393        if self.if_exists {
3394            f.write_str("IF EXISTS ");
3395        }
3396        f.write_node(&self.name);
3397
3398        f.write_str(" APPLY REPLACEMENT ");
3399        f.write_node(&self.replacement_name);
3400    }
3401}
3402
3403impl_display!(AlterMaterializedViewApplyReplacementStatement);
3404
3405#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3406pub struct DiscardStatement {
3407    pub target: DiscardTarget,
3408}
3409
3410impl AstDisplay for DiscardStatement {
3411    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3412        f.write_str("DISCARD ");
3413        f.write_node(&self.target);
3414    }
3415}
3416impl_display!(DiscardStatement);
3417
3418#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3419pub enum DiscardTarget {
3420    Plans,
3421    Sequences,
3422    Temp,
3423    All,
3424}
3425
3426impl AstDisplay for DiscardTarget {
3427    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3428        match self {
3429            DiscardTarget::Plans => f.write_str("PLANS"),
3430            DiscardTarget::Sequences => f.write_str("SEQUENCES"),
3431            DiscardTarget::Temp => f.write_str("TEMP"),
3432            DiscardTarget::All => f.write_str("ALL"),
3433        }
3434    }
3435}
3436impl_display!(DiscardTarget);
3437
3438/// `DROP`
3439#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3440pub struct DropObjectsStatement {
3441    /// The type of the object to drop: TABLE, VIEW, etc.
3442    pub object_type: ObjectType,
3443    /// An optional `IF EXISTS` clause. (Non-standard.)
3444    pub if_exists: bool,
3445    /// One or more objects to drop. (ANSI SQL requires exactly one.)
3446    pub names: Vec<UnresolvedObjectName>,
3447    /// Whether `CASCADE` was specified. This will be `false` when
3448    /// `RESTRICT` was specified.
3449    pub cascade: bool,
3450}
3451
3452impl AstDisplay for DropObjectsStatement {
3453    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3454        f.write_str("DROP ");
3455        f.write_node(&self.object_type);
3456        f.write_str(" ");
3457        if self.if_exists {
3458            f.write_str("IF EXISTS ");
3459        }
3460        f.write_node(&display::comma_separated(&self.names));
3461        if self.cascade && self.object_type != ObjectType::Database {
3462            f.write_str(" CASCADE");
3463        } else if !self.cascade && self.object_type == ObjectType::Database {
3464            f.write_str(" RESTRICT");
3465        }
3466    }
3467}
3468impl_display!(DropObjectsStatement);
3469
3470/// `DROP OWNED BY ...`
3471#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3472pub struct DropOwnedStatement<T: AstInfo> {
3473    /// The roles whose owned objects are being dropped.
3474    pub role_names: Vec<T::RoleName>,
3475    /// Whether `CASCADE` was specified. `false` for `RESTRICT` and `None` if no drop behavior at
3476    /// all was specified.
3477    pub cascade: Option<bool>,
3478}
3479
3480impl<T: AstInfo> DropOwnedStatement<T> {
3481    pub fn cascade(&self) -> bool {
3482        self.cascade == Some(true)
3483    }
3484}
3485
3486impl<T: AstInfo> AstDisplay for DropOwnedStatement<T> {
3487    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3488        f.write_str("DROP OWNED BY ");
3489        f.write_node(&display::comma_separated(&self.role_names));
3490        if let Some(true) = self.cascade {
3491            f.write_str(" CASCADE");
3492        } else if let Some(false) = self.cascade {
3493            f.write_str(" RESTRICT");
3494        }
3495    }
3496}
3497impl_display_t!(DropOwnedStatement);
3498
3499#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3500pub struct QualifiedReplica {
3501    pub cluster: Ident,
3502    pub replica: Ident,
3503}
3504
3505impl AstDisplay for QualifiedReplica {
3506    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3507        f.write_node(&self.cluster);
3508        f.write_str(".");
3509        f.write_node(&self.replica);
3510    }
3511}
3512impl_display!(QualifiedReplica);
3513
3514/// `SET <variable>`
3515///
3516/// Note: this is not a standard SQL statement, but it is supported by at
3517/// least MySQL and PostgreSQL. Not all MySQL-specific syntactic forms are
3518/// supported yet.
3519#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3520pub struct SetVariableStatement {
3521    pub local: bool,
3522    pub variable: Ident,
3523    pub to: SetVariableTo,
3524}
3525
3526impl AstDisplay for SetVariableStatement {
3527    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3528        f.write_str("SET ");
3529        if self.local {
3530            f.write_str("LOCAL ");
3531        }
3532        f.write_node(&self.variable);
3533        f.write_str(" = ");
3534        f.write_node(&self.to);
3535    }
3536}
3537impl_display!(SetVariableStatement);
3538
3539/// `RESET <variable>`
3540///
3541/// Note: this is not a standard SQL statement, but it is supported by at
3542/// least MySQL and PostgreSQL. Not all syntactic forms are supported yet.
3543#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3544pub struct ResetVariableStatement {
3545    pub variable: Ident,
3546}
3547
3548impl AstDisplay for ResetVariableStatement {
3549    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3550        f.write_str("RESET ");
3551        f.write_node(&self.variable);
3552    }
3553}
3554impl_display!(ResetVariableStatement);
3555
3556/// `SHOW <variable>`
3557#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3558pub struct ShowVariableStatement {
3559    pub variable: Ident,
3560}
3561
3562impl AstDisplay for ShowVariableStatement {
3563    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3564        f.write_str("SHOW ");
3565        f.write_node(&self.variable);
3566    }
3567}
3568impl_display!(ShowVariableStatement);
3569
3570/// `INSPECT SHARD <id>`
3571#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3572pub struct InspectShardStatement {
3573    pub id: String,
3574}
3575
3576impl AstDisplay for InspectShardStatement {
3577    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3578        f.write_str("INSPECT SHARD ");
3579        f.write_str("'");
3580        f.write_node(&display::escape_single_quote_string(&self.id));
3581        f.write_str("'");
3582    }
3583}
3584impl_display!(InspectShardStatement);
3585
3586#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3587pub enum ShowObjectType<T: AstInfo> {
3588    MaterializedView {
3589        in_cluster: Option<T::ClusterName>,
3590    },
3591    Index {
3592        in_cluster: Option<T::ClusterName>,
3593        on_object: Option<T::ItemName>,
3594    },
3595    Table {
3596        on_source: Option<T::ItemName>,
3597    },
3598    View,
3599    Source {
3600        in_cluster: Option<T::ClusterName>,
3601    },
3602    Sink {
3603        in_cluster: Option<T::ClusterName>,
3604    },
3605    MetricSink {
3606        in_cluster: Option<T::ClusterName>,
3607    },
3608    Type,
3609    Role,
3610    Cluster,
3611    ClusterReplica,
3612    Object,
3613    Secret,
3614    Connection,
3615    Database,
3616    Schema {
3617        from: Option<T::DatabaseName>,
3618    },
3619    Subsource {
3620        on_source: Option<T::ItemName>,
3621    },
3622    Privileges {
3623        object_type: Option<SystemObjectType>,
3624        role: Option<T::RoleName>,
3625    },
3626    DefaultPrivileges {
3627        object_type: Option<ObjectType>,
3628        role: Option<T::RoleName>,
3629    },
3630    RoleMembership {
3631        role: Option<T::RoleName>,
3632    },
3633    NetworkPolicy,
3634}
3635/// `SHOW <object>S`
3636///
3637/// ```sql
3638/// SHOW TABLES;
3639/// SHOW SOURCES;
3640/// SHOW VIEWS;
3641/// SHOW SINKS;
3642/// ```
3643#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3644pub struct ShowObjectsStatement<T: AstInfo> {
3645    pub object_type: ShowObjectType<T>,
3646    pub from: Option<T::SchemaName>,
3647    pub filter: Option<ShowStatementFilter<T>>,
3648}
3649
3650impl<T: AstInfo> AstDisplay for ShowObjectsStatement<T> {
3651    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3652        f.write_str("SHOW");
3653        f.write_str(" ");
3654
3655        f.write_str(match &self.object_type {
3656            ShowObjectType::Table { .. } => "TABLES",
3657            ShowObjectType::View => "VIEWS",
3658            ShowObjectType::Source { .. } => "SOURCES",
3659            ShowObjectType::Sink { .. } => "SINKS",
3660            ShowObjectType::MetricSink { .. } => "METRIC SINKS",
3661            ShowObjectType::Type => "TYPES",
3662            ShowObjectType::Role => "ROLES",
3663            ShowObjectType::Cluster => "CLUSTERS",
3664            ShowObjectType::ClusterReplica => "CLUSTER REPLICAS",
3665            ShowObjectType::Object => "OBJECTS",
3666            ShowObjectType::Secret => "SECRETS",
3667            ShowObjectType::Connection => "CONNECTIONS",
3668            ShowObjectType::MaterializedView { .. } => "MATERIALIZED VIEWS",
3669            ShowObjectType::Index { .. } => "INDEXES",
3670            ShowObjectType::Database => "DATABASES",
3671            ShowObjectType::Schema { .. } => "SCHEMAS",
3672            ShowObjectType::Subsource { .. } => "SUBSOURCES",
3673            ShowObjectType::Privileges { .. } => "PRIVILEGES",
3674            ShowObjectType::DefaultPrivileges { .. } => "DEFAULT PRIVILEGES",
3675            ShowObjectType::RoleMembership { .. } => "ROLE MEMBERSHIP",
3676            ShowObjectType::NetworkPolicy => "NETWORK POLICIES",
3677        });
3678
3679        if let ShowObjectType::Index { on_object, .. } = &self.object_type {
3680            if let Some(on_object) = on_object {
3681                f.write_str(" ON ");
3682                f.write_node(on_object);
3683            }
3684        }
3685
3686        if let ShowObjectType::Schema { from: Some(from) } = &self.object_type {
3687            f.write_str(" FROM ");
3688            f.write_node(from);
3689        }
3690
3691        if let Some(from) = &self.from {
3692            f.write_str(" FROM ");
3693            f.write_node(from);
3694        }
3695
3696        // append IN CLUSTER clause
3697        match &self.object_type {
3698            ShowObjectType::MaterializedView { in_cluster }
3699            | ShowObjectType::Index { in_cluster, .. }
3700            | ShowObjectType::Sink { in_cluster }
3701            | ShowObjectType::MetricSink { in_cluster }
3702            | ShowObjectType::Source { in_cluster } => {
3703                if let Some(cluster) = in_cluster {
3704                    f.write_str(" IN CLUSTER ");
3705                    f.write_node(cluster);
3706                }
3707            }
3708            _ => (),
3709        }
3710
3711        if let ShowObjectType::Subsource { on_source } = &self.object_type {
3712            if let Some(on_source) = on_source {
3713                f.write_str(" ON ");
3714                f.write_node(on_source);
3715            }
3716        }
3717
3718        if let ShowObjectType::Table { on_source } = &self.object_type {
3719            if let Some(on_source) = on_source {
3720                f.write_str(" ON ");
3721                f.write_node(on_source);
3722            }
3723        }
3724
3725        if let ShowObjectType::Privileges { object_type, role } = &self.object_type {
3726            if let Some(object_type) = object_type {
3727                f.write_str(" ON ");
3728                f.write_node(object_type);
3729                if let SystemObjectType::Object(_) = object_type {
3730                    f.write_str("S");
3731                }
3732            }
3733            if let Some(role) = role {
3734                f.write_str(" FOR ");
3735                f.write_node(role);
3736            }
3737        }
3738
3739        if let ShowObjectType::DefaultPrivileges { object_type, role } = &self.object_type {
3740            if let Some(object_type) = object_type {
3741                f.write_str(" ON ");
3742                f.write_node(object_type);
3743                f.write_str("S");
3744            }
3745            if let Some(role) = role {
3746                f.write_str(" FOR ");
3747                f.write_node(role);
3748            }
3749        }
3750
3751        if let ShowObjectType::RoleMembership {
3752            role: Some(role), ..
3753        } = &self.object_type
3754        {
3755            f.write_str(" FOR ");
3756            f.write_node(role);
3757        }
3758
3759        if let Some(filter) = &self.filter {
3760            f.write_str(" ");
3761            f.write_node(filter);
3762        }
3763    }
3764}
3765impl_display_t!(ShowObjectsStatement);
3766
3767/// `SHOW COLUMNS`
3768///
3769/// Note: this is a MySQL-specific statement.
3770#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3771pub struct ShowColumnsStatement<T: AstInfo> {
3772    pub table_name: T::ItemName,
3773    pub filter: Option<ShowStatementFilter<T>>,
3774}
3775
3776impl<T: AstInfo> AstDisplay for ShowColumnsStatement<T> {
3777    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3778        f.write_str("SHOW ");
3779        f.write_str("COLUMNS FROM ");
3780        f.write_node(&self.table_name);
3781        if let Some(filter) = &self.filter {
3782            f.write_str(" ");
3783            f.write_node(filter);
3784        }
3785    }
3786}
3787impl_display_t!(ShowColumnsStatement);
3788
3789/// `SHOW [REDACTED] CREATE VIEW <view>`
3790#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3791pub struct ShowCreateViewStatement<T: AstInfo> {
3792    pub view_name: T::ItemName,
3793    pub redacted: bool,
3794}
3795
3796impl<T: AstInfo> AstDisplay for ShowCreateViewStatement<T> {
3797    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3798        f.write_str("SHOW ");
3799        if self.redacted {
3800            f.write_str("REDACTED ");
3801        }
3802        f.write_str("CREATE VIEW ");
3803        f.write_node(&self.view_name);
3804    }
3805}
3806impl_display_t!(ShowCreateViewStatement);
3807
3808/// `SHOW [REDACTED] CREATE MATERIALIZED VIEW <name>`
3809#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3810pub struct ShowCreateMaterializedViewStatement<T: AstInfo> {
3811    pub materialized_view_name: T::ItemName,
3812    pub redacted: bool,
3813}
3814
3815impl<T: AstInfo> AstDisplay for ShowCreateMaterializedViewStatement<T> {
3816    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3817        f.write_str("SHOW ");
3818        if self.redacted {
3819            f.write_str("REDACTED ");
3820        }
3821        f.write_str("CREATE MATERIALIZED VIEW ");
3822        f.write_node(&self.materialized_view_name);
3823    }
3824}
3825impl_display_t!(ShowCreateMaterializedViewStatement);
3826
3827/// `SHOW [REDACTED] CREATE SOURCE <source>`
3828#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3829pub struct ShowCreateSourceStatement<T: AstInfo> {
3830    pub source_name: T::ItemName,
3831    pub redacted: bool,
3832}
3833
3834impl<T: AstInfo> AstDisplay for ShowCreateSourceStatement<T> {
3835    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3836        f.write_str("SHOW ");
3837        if self.redacted {
3838            f.write_str("REDACTED ");
3839        }
3840        f.write_str("CREATE SOURCE ");
3841        f.write_node(&self.source_name);
3842    }
3843}
3844impl_display_t!(ShowCreateSourceStatement);
3845
3846/// `SHOW [REDACTED] CREATE TABLE <table>`
3847#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3848pub struct ShowCreateTableStatement<T: AstInfo> {
3849    pub table_name: T::ItemName,
3850    pub redacted: bool,
3851}
3852
3853impl<T: AstInfo> AstDisplay for ShowCreateTableStatement<T> {
3854    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3855        f.write_str("SHOW ");
3856        if self.redacted {
3857            f.write_str("REDACTED ");
3858        }
3859        f.write_str("CREATE TABLE ");
3860        f.write_node(&self.table_name);
3861    }
3862}
3863impl_display_t!(ShowCreateTableStatement);
3864
3865/// `SHOW [REDACTED] CREATE SINK <sink>`
3866#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3867pub struct ShowCreateSinkStatement<T: AstInfo> {
3868    pub sink_name: T::ItemName,
3869    pub redacted: bool,
3870}
3871
3872impl<T: AstInfo> AstDisplay for ShowCreateSinkStatement<T> {
3873    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3874        f.write_str("SHOW ");
3875        if self.redacted {
3876            f.write_str("REDACTED ");
3877        }
3878        f.write_str("CREATE SINK ");
3879        f.write_node(&self.sink_name);
3880    }
3881}
3882impl_display_t!(ShowCreateSinkStatement);
3883
3884/// `SHOW [REDACTED] CREATE METRIC SINK <sink>`
3885#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3886pub struct ShowCreateMetricSinkStatement<T: AstInfo> {
3887    pub metric_sink_name: T::ItemName,
3888    pub redacted: bool,
3889}
3890
3891impl<T: AstInfo> AstDisplay for ShowCreateMetricSinkStatement<T> {
3892    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3893        f.write_str("SHOW ");
3894        if self.redacted {
3895            f.write_str("REDACTED ");
3896        }
3897        f.write_str("CREATE METRIC SINK ");
3898        f.write_node(&self.metric_sink_name);
3899    }
3900}
3901impl_display_t!(ShowCreateMetricSinkStatement);
3902
3903/// `SHOW [REDACTED] CREATE INDEX <index>`
3904#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3905pub struct ShowCreateIndexStatement<T: AstInfo> {
3906    pub index_name: T::ItemName,
3907    pub redacted: bool,
3908}
3909
3910impl<T: AstInfo> AstDisplay for ShowCreateIndexStatement<T> {
3911    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3912        f.write_str("SHOW ");
3913        if self.redacted {
3914            f.write_str("REDACTED ");
3915        }
3916        f.write_str("CREATE INDEX ");
3917        f.write_node(&self.index_name);
3918    }
3919}
3920impl_display_t!(ShowCreateIndexStatement);
3921
3922/// `SHOW [REDACTED] CREATE CONNECTION <connection>`
3923#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3924pub struct ShowCreateConnectionStatement<T: AstInfo> {
3925    pub connection_name: T::ItemName,
3926    pub redacted: bool,
3927}
3928
3929impl<T: AstInfo> AstDisplay for ShowCreateConnectionStatement<T> {
3930    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3931        f.write_str("SHOW ");
3932        if self.redacted {
3933            f.write_str("REDACTED ");
3934        }
3935        f.write_str("CREATE CONNECTION ");
3936        f.write_node(&self.connection_name);
3937    }
3938}
3939
3940#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3941pub struct ShowCreateClusterStatement<T: AstInfo> {
3942    pub cluster_name: T::ClusterName,
3943}
3944
3945impl<T: AstInfo> AstDisplay for ShowCreateClusterStatement<T> {
3946    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3947        f.write_str("SHOW CREATE CLUSTER ");
3948        f.write_node(&self.cluster_name);
3949    }
3950}
3951
3952/// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...`
3953#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3954pub struct StartTransactionStatement {
3955    pub modes: Vec<TransactionMode>,
3956}
3957
3958impl AstDisplay for StartTransactionStatement {
3959    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3960        f.write_str("START TRANSACTION");
3961        if !self.modes.is_empty() {
3962            f.write_str(" ");
3963            f.write_node(&display::comma_separated(&self.modes));
3964        }
3965    }
3966}
3967impl_display!(StartTransactionStatement);
3968
3969/// `SHOW [REDACTED] CREATE TYPE <type>`
3970#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3971pub struct ShowCreateTypeStatement<T: AstInfo> {
3972    pub type_name: T::DataType,
3973    pub redacted: bool,
3974}
3975
3976impl<T: AstInfo> AstDisplay for ShowCreateTypeStatement<T> {
3977    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3978        f.write_str("SHOW ");
3979        if self.redacted {
3980            f.write_str("REDACTED ");
3981        }
3982        f.write_str("CREATE TYPE ");
3983        f.write_node(&self.type_name);
3984    }
3985}
3986
3987/// `SET TRANSACTION ...`
3988#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3989pub struct SetTransactionStatement {
3990    pub local: bool,
3991    pub modes: Vec<TransactionMode>,
3992}
3993
3994impl AstDisplay for SetTransactionStatement {
3995    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3996        f.write_str("SET ");
3997        if !self.local {
3998            f.write_str("SESSION CHARACTERISTICS AS ");
3999        }
4000        f.write_str("TRANSACTION");
4001        if !self.modes.is_empty() {
4002            f.write_str(" ");
4003            f.write_node(&display::comma_separated(&self.modes));
4004        }
4005    }
4006}
4007impl_display!(SetTransactionStatement);
4008
4009/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
4010#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4011pub struct CommitStatement {
4012    pub chain: bool,
4013}
4014
4015impl AstDisplay for CommitStatement {
4016    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4017        f.write_str("COMMIT");
4018        if self.chain {
4019            f.write_str(" AND CHAIN");
4020        }
4021    }
4022}
4023impl_display!(CommitStatement);
4024
4025/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
4026#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4027pub struct RollbackStatement {
4028    pub chain: bool,
4029}
4030
4031impl AstDisplay for RollbackStatement {
4032    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4033        f.write_str("ROLLBACK");
4034        if self.chain {
4035            f.write_str(" AND CHAIN");
4036        }
4037    }
4038}
4039impl_display!(RollbackStatement);
4040
4041#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4042pub enum SubscribeOptionName {
4043    Snapshot,
4044    Progress,
4045}
4046
4047impl AstDisplay for SubscribeOptionName {
4048    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4049        match self {
4050            SubscribeOptionName::Snapshot => f.write_str("SNAPSHOT"),
4051            SubscribeOptionName::Progress => f.write_str("PROGRESS"),
4052        }
4053    }
4054}
4055impl_display!(SubscribeOptionName);
4056
4057impl WithOptionName for SubscribeOptionName {
4058    /// # WARNING
4059    ///
4060    /// Whenever implementing this trait consider very carefully whether or not
4061    /// this value could contain sensitive user data. If you're uncertain, err
4062    /// on the conservative side and return `true`.
4063    fn redact_value(&self) -> bool {
4064        match self {
4065            SubscribeOptionName::Snapshot | SubscribeOptionName::Progress => false,
4066        }
4067    }
4068}
4069
4070#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4071pub struct SubscribeOption<T: AstInfo> {
4072    pub name: SubscribeOptionName,
4073    pub value: Option<WithOptionValue<T>>,
4074}
4075impl_display_for_with_option!(SubscribeOption);
4076impl_display_t!(SubscribeOption);
4077
4078/// `SUBSCRIBE`
4079#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4080pub struct SubscribeStatement<T: AstInfo> {
4081    pub relation: SubscribeRelation<T>,
4082    pub options: Vec<SubscribeOption<T>>,
4083    pub as_of: Option<AsOf<T>>,
4084    pub up_to: Option<Expr<T>>,
4085    pub output: SubscribeOutput<T>,
4086}
4087
4088impl<T: AstInfo> AstDisplay for SubscribeStatement<T> {
4089    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4090        f.write_str("SUBSCRIBE ");
4091        if self.relation.needs_explicit_to(f.simple()) {
4092            // Without the optional `TO` keyword, a relation whose first name
4093            // component is the bare keyword `to` (e.g. `SUBSCRIBE TO to`) would
4094            // display as `SUBSCRIBE to`, which re-parses with `to` consumed as
4095            // the optional keyword, dropping the relation name.
4096            f.write_str("TO ");
4097        }
4098        f.write_node(&self.relation);
4099        if !self.options.is_empty() {
4100            f.write_str(" WITH (");
4101            f.write_node(&display::comma_separated(&self.options));
4102            f.write_str(")");
4103        }
4104        if let Some(as_of) = &self.as_of {
4105            f.write_str(" ");
4106            f.write_node(as_of);
4107        }
4108        if let Some(up_to) = &self.up_to {
4109            f.write_str(" UP TO ");
4110            f.write_node(up_to);
4111        }
4112        f.write_str(&self.output);
4113    }
4114}
4115impl_display_t!(SubscribeStatement);
4116
4117#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4118pub enum SubscribeRelation<T: AstInfo> {
4119    Name(T::ItemName),
4120    Query(Query<T>),
4121}
4122
4123impl<T: AstInfo> SubscribeRelation<T> {
4124    /// Reports whether printing this relation after `SUBSCRIBE` requires the
4125    /// optional `TO` keyword to avoid reparsing the first name component as that
4126    /// keyword instead of as part of the relation name.
4127    pub fn needs_explicit_to(&self, bare_identifiers: bool) -> bool {
4128        let SubscribeRelation::Name(name) = self else {
4129            return false;
4130        };
4131        bare_identifiers && name_starts_with_bare_to(&name.to_ast_string_simple())
4132    }
4133}
4134
4135fn name_starts_with_bare_to(name: &str) -> bool {
4136    let Some(rest) = name.strip_prefix("to") else {
4137        return false;
4138    };
4139    rest.is_empty() || rest.starts_with('.')
4140}
4141
4142impl<T: AstInfo> AstDisplay for SubscribeRelation<T> {
4143    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4144        match self {
4145            SubscribeRelation::Name(name) => f.write_node(name),
4146            SubscribeRelation::Query(query) => {
4147                f.write_str("(");
4148                f.write_node(query);
4149                f.write_str(")");
4150            }
4151        }
4152    }
4153}
4154impl_display_t!(SubscribeRelation);
4155
4156#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4157pub struct ExplainPlanStatement<T: AstInfo> {
4158    pub stage: Option<ExplainStage>,
4159    pub with_options: Vec<ExplainPlanOption<T>>,
4160    pub format: Option<ExplainFormat>,
4161    pub explainee: Explainee<T>,
4162}
4163
4164impl<T: AstInfo> ExplainPlanStatement<T> {
4165    pub fn stage(&self) -> ExplainStage {
4166        self.stage.unwrap_or(ExplainStage::PhysicalPlan)
4167    }
4168
4169    pub fn format(&self) -> ExplainFormat {
4170        self.format.unwrap_or(ExplainFormat::Text)
4171    }
4172}
4173
4174impl<T: AstInfo> AstDisplay for ExplainPlanStatement<T> {
4175    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4176        f.write_str("EXPLAIN");
4177        if let Some(stage) = &self.stage {
4178            f.write_str(" ");
4179            f.write_node(stage);
4180        }
4181        if !self.with_options.is_empty() {
4182            f.write_str(" WITH (");
4183            f.write_node(&display::comma_separated(&self.with_options));
4184            f.write_str(")");
4185        }
4186        if let Some(format) = &self.format {
4187            f.write_str(" AS ");
4188            f.write_node(format);
4189        }
4190        if self.stage.is_some() {
4191            f.write_str(" FOR");
4192        }
4193        f.write_str(" ");
4194        f.write_node(&self.explainee);
4195    }
4196}
4197impl_display_t!(ExplainPlanStatement);
4198
4199// Note: the `AstDisplay` implementation and `Parser::parse_` method for this
4200// enum are generated automatically by this crate's `build.rs`.
4201#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4202pub enum ExplainPlanOptionName {
4203    Arity,
4204    Cardinality,
4205    ColumnNames,
4206    FilterPushdown,
4207    HumanizedExpressions,
4208    JoinImplementations,
4209    Keys,
4210    LinearChains,
4211    NonNegative,
4212    NoFastPath,
4213    NoNotices,
4214    NodeIdentifiers,
4215    RawPlans,
4216    RawSyntax,
4217    Raw, // Listed after the `Raw~` variants to keep the parser happy!
4218    Redacted,
4219    SubtreeSize,
4220    Timing,
4221    Types,
4222    Equivalences,
4223    ReoptimizeImportedViews,
4224    EnableNewOuterJoinLowering,
4225    EnableEagerDeltaJoins,
4226    EnableVariadicLeftJoinLowering,
4227    EnableLetrecFixpointAnalysis,
4228    EnableJoinPrioritizeArranged,
4229    EnableProjectionPushdownAfterRelationCse,
4230    EnableFixedCorrelatedCteLowering,
4231    EnableUnionCancellationAfterRelationCse,
4232}
4233
4234impl WithOptionName for ExplainPlanOptionName {
4235    /// # WARNING
4236    ///
4237    /// Whenever implementing this trait consider very carefully whether or not
4238    /// this value could contain sensitive user data. If you're uncertain, err
4239    /// on the conservative side and return `true`.
4240    fn redact_value(&self) -> bool {
4241        match self {
4242            Self::Arity
4243            | Self::Cardinality
4244            | Self::ColumnNames
4245            | Self::FilterPushdown
4246            | Self::HumanizedExpressions
4247            | Self::JoinImplementations
4248            | Self::Keys
4249            | Self::LinearChains
4250            | Self::NonNegative
4251            | Self::NoFastPath
4252            | Self::NoNotices
4253            | Self::NodeIdentifiers
4254            | Self::RawPlans
4255            | Self::RawSyntax
4256            | Self::Raw
4257            | Self::Redacted
4258            | Self::SubtreeSize
4259            | Self::Timing
4260            | Self::Types
4261            | Self::Equivalences
4262            | Self::ReoptimizeImportedViews
4263            | Self::EnableNewOuterJoinLowering
4264            | Self::EnableEagerDeltaJoins
4265            | Self::EnableVariadicLeftJoinLowering
4266            | Self::EnableLetrecFixpointAnalysis
4267            | Self::EnableJoinPrioritizeArranged
4268            | Self::EnableProjectionPushdownAfterRelationCse
4269            | Self::EnableFixedCorrelatedCteLowering
4270            | Self::EnableUnionCancellationAfterRelationCse => false,
4271        }
4272    }
4273}
4274
4275#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4276pub struct ExplainPlanOption<T: AstInfo> {
4277    pub name: ExplainPlanOptionName,
4278    pub value: Option<WithOptionValue<T>>,
4279}
4280impl_display_for_with_option!(ExplainPlanOption);
4281
4282#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4283pub enum ExplainSinkSchemaFor {
4284    Key,
4285    Value,
4286}
4287#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4288pub struct ExplainSinkSchemaStatement<T: AstInfo> {
4289    pub schema_for: ExplainSinkSchemaFor,
4290    pub format: Option<ExplainFormat>,
4291    pub statement: CreateSinkStatement<T>,
4292}
4293
4294impl<T: AstInfo> AstDisplay for ExplainSinkSchemaStatement<T> {
4295    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4296        f.write_str("EXPLAIN ");
4297        match &self.schema_for {
4298            ExplainSinkSchemaFor::Key => f.write_str("KEY"),
4299            ExplainSinkSchemaFor::Value => f.write_str("VALUE"),
4300        }
4301        f.write_str(" SCHEMA");
4302        if let Some(format) = &self.format {
4303            f.write_str(" AS ");
4304            f.write_node(format);
4305        }
4306        f.write_str(" FOR ");
4307        f.write_node(&self.statement);
4308    }
4309}
4310impl_display_t!(ExplainSinkSchemaStatement);
4311
4312#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4313pub struct ExplainPushdownStatement<T: AstInfo> {
4314    pub explainee: Explainee<T>,
4315}
4316
4317impl<T: AstInfo> AstDisplay for ExplainPushdownStatement<T> {
4318    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4319        f.write_str("EXPLAIN FILTER PUSHDOWN FOR ");
4320        f.write_node(&self.explainee);
4321    }
4322}
4323impl_display_t!(ExplainPushdownStatement);
4324
4325#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4326pub enum ExplainAnalyzeComputationProperty {
4327    Cpu,
4328    Memory,
4329}
4330
4331#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4332pub enum ExplainAnalyzeProperty {
4333    Computation(ExplainAnalyzeComputationProperties),
4334    Hints,
4335}
4336
4337#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4338pub struct ExplainAnalyzeComputationProperties {
4339    /// Must be non-empty.
4340    pub properties: Vec<ExplainAnalyzeComputationProperty>,
4341    pub skew: bool,
4342}
4343#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4344pub struct ExplainAnalyzeObjectStatement<T: AstInfo> {
4345    pub properties: ExplainAnalyzeProperty,
4346    /// Should only be `Explainee::Index` or `Explainee::MaterializedView`
4347    pub explainee: Explainee<T>,
4348    pub as_sql: bool,
4349}
4350
4351impl<T: AstInfo> AstDisplay for ExplainAnalyzeObjectStatement<T> {
4352    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4353        f.write_str("EXPLAIN ANALYZE");
4354        match &self.properties {
4355            ExplainAnalyzeProperty::Computation(ExplainAnalyzeComputationProperties {
4356                properties,
4357                skew,
4358            }) => {
4359                let mut first = true;
4360                for property in properties {
4361                    if first {
4362                        first = false;
4363                    } else {
4364                        f.write_str(",");
4365                    }
4366                    match property {
4367                        ExplainAnalyzeComputationProperty::Cpu => f.write_str(" CPU"),
4368                        ExplainAnalyzeComputationProperty::Memory => f.write_str(" MEMORY"),
4369                    }
4370                }
4371                if *skew {
4372                    f.write_str(" WITH SKEW");
4373                }
4374            }
4375            ExplainAnalyzeProperty::Hints => f.write_str(" HINTS"),
4376        }
4377        f.write_str(" FOR ");
4378        f.write_node(&self.explainee);
4379        if self.as_sql {
4380            f.write_str(" AS SQL");
4381        }
4382    }
4383}
4384impl_display_t!(ExplainAnalyzeObjectStatement);
4385
4386#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4387pub struct ExplainAnalyzeClusterStatement {
4388    pub properties: ExplainAnalyzeComputationProperties,
4389    pub as_sql: bool,
4390}
4391
4392impl AstDisplay for ExplainAnalyzeClusterStatement {
4393    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4394        f.write_str("EXPLAIN ANALYZE CLUSTER");
4395
4396        let mut first = true;
4397        for property in &self.properties.properties {
4398            if first {
4399                first = false;
4400            } else {
4401                f.write_str(",");
4402            }
4403            match property {
4404                ExplainAnalyzeComputationProperty::Cpu => f.write_str(" CPU"),
4405                ExplainAnalyzeComputationProperty::Memory => f.write_str(" MEMORY"),
4406            }
4407        }
4408
4409        if self.properties.skew {
4410            f.write_str(" WITH SKEW");
4411        }
4412        if self.as_sql {
4413            f.write_str(" AS SQL");
4414        }
4415    }
4416}
4417impl_display!(ExplainAnalyzeClusterStatement);
4418
4419#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4420pub struct ExplainTimestampStatement<T: AstInfo> {
4421    pub format: Option<ExplainFormat>,
4422    pub select: SelectStatement<T>,
4423}
4424
4425impl<T: AstInfo> ExplainTimestampStatement<T> {
4426    pub fn format(&self) -> ExplainFormat {
4427        self.format.unwrap_or(ExplainFormat::Text)
4428    }
4429}
4430
4431impl<T: AstInfo> AstDisplay for ExplainTimestampStatement<T> {
4432    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4433        f.write_str("EXPLAIN TIMESTAMP");
4434        if let Some(format) = &self.format {
4435            f.write_str(" AS ");
4436            f.write_node(format);
4437        }
4438        f.write_str(" FOR ");
4439        f.write_node(&self.select);
4440    }
4441}
4442impl_display_t!(ExplainTimestampStatement);
4443
4444#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4445pub enum InsertSource<T: AstInfo> {
4446    Query(Query<T>),
4447    DefaultValues,
4448}
4449
4450impl<T: AstInfo> AstDisplay for InsertSource<T> {
4451    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4452        match self {
4453            InsertSource::Query(query) => f.write_node(query),
4454            InsertSource::DefaultValues => f.write_str("DEFAULT VALUES"),
4455        }
4456    }
4457}
4458impl_display_t!(InsertSource);
4459
4460#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
4461pub enum ObjectType {
4462    Table,
4463    View,
4464    MaterializedView,
4465    Source,
4466    Sink,
4467    MetricSink,
4468    Index,
4469    Type,
4470    Role,
4471    Cluster,
4472    ClusterReplica,
4473    Secret,
4474    Connection,
4475    Database,
4476    Schema,
4477    Func,
4478    Subsource,
4479    NetworkPolicy,
4480}
4481
4482impl ObjectType {
4483    pub fn lives_in_schema(&self) -> bool {
4484        match self {
4485            ObjectType::Table
4486            | ObjectType::View
4487            | ObjectType::MaterializedView
4488            | ObjectType::Source
4489            | ObjectType::Sink
4490            | ObjectType::MetricSink
4491            | ObjectType::Index
4492            | ObjectType::Type
4493            | ObjectType::Secret
4494            | ObjectType::Connection
4495            | ObjectType::Func
4496            | ObjectType::Subsource => true,
4497            ObjectType::Database
4498            | ObjectType::Schema
4499            | ObjectType::Cluster
4500            | ObjectType::ClusterReplica
4501            | ObjectType::Role
4502            | ObjectType::NetworkPolicy => false,
4503        }
4504    }
4505}
4506
4507impl AstDisplay for ObjectType {
4508    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4509        f.write_str(match self {
4510            ObjectType::Table => "TABLE",
4511            ObjectType::View => "VIEW",
4512            ObjectType::MaterializedView => "MATERIALIZED VIEW",
4513            ObjectType::Source => "SOURCE",
4514            ObjectType::Sink => "SINK",
4515            ObjectType::MetricSink => "METRIC SINK",
4516            ObjectType::Index => "INDEX",
4517            ObjectType::Type => "TYPE",
4518            ObjectType::Role => "ROLE",
4519            ObjectType::Cluster => "CLUSTER",
4520            ObjectType::ClusterReplica => "CLUSTER REPLICA",
4521            ObjectType::Secret => "SECRET",
4522            ObjectType::Connection => "CONNECTION",
4523            ObjectType::Database => "DATABASE",
4524            ObjectType::Schema => "SCHEMA",
4525            ObjectType::Func => "FUNCTION",
4526            ObjectType::Subsource => "SUBSOURCE",
4527            ObjectType::NetworkPolicy => "NETWORK POLICY",
4528        })
4529    }
4530}
4531impl_display!(ObjectType);
4532
4533#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
4534pub enum SystemObjectType {
4535    System,
4536    Object(ObjectType),
4537}
4538
4539impl AstDisplay for SystemObjectType {
4540    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4541        match self {
4542            SystemObjectType::System => f.write_str("SYSTEM"),
4543            SystemObjectType::Object(object) => f.write_node(object),
4544        }
4545    }
4546}
4547impl_display!(SystemObjectType);
4548
4549#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4550pub enum ShowStatementFilter<T: AstInfo> {
4551    Like(String),
4552    Where(Expr<T>),
4553}
4554
4555impl<T: AstInfo> AstDisplay for ShowStatementFilter<T> {
4556    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4557        use ShowStatementFilter::*;
4558        match self {
4559            Like(pattern) => {
4560                f.write_str("LIKE '");
4561                f.write_node(&display::escape_single_quote_string(pattern));
4562                f.write_str("'");
4563            }
4564            Where(expr) => {
4565                f.write_str("WHERE ");
4566                f.write_node(expr);
4567            }
4568        }
4569    }
4570}
4571impl_display_t!(ShowStatementFilter);
4572
4573#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4574pub enum WithOptionValue<T: AstInfo> {
4575    Value(Value),
4576    DataType(T::DataType),
4577    Secret(T::ItemName),
4578    Item(T::ItemName),
4579    UnresolvedItemName(UnresolvedItemName),
4580    Ident(Ident),
4581    Sequence(Vec<WithOptionValue<T>>),
4582    Map(BTreeMap<String, WithOptionValue<T>>),
4583    // Special cases.
4584    Expr(Expr<T>),
4585    ClusterReplicas(Vec<ReplicaDefinition<T>>),
4586    ConnectionKafkaBroker(KafkaBroker<T>),
4587    ConnectionAwsPrivatelink(ConnectionDefaultAwsPrivatelink<T>),
4588    KafkaMatchingBrokerRule(KafkaMatchingBrokerRule<T>),
4589    RetainHistoryFor(Value),
4590    Refresh(RefreshOptionValue<T>),
4591    ClusterScheduleOptionValue(ClusterScheduleOptionValue),
4592    ClusterAutoScalingStrategyOptionValue(ClusterAutoScalingStrategyOptionValue),
4593    ClusterAlterStrategy(ClusterAlterOptionValue<T>),
4594    NetworkPolicyRules(Vec<NetworkPolicyRuleDefinition<T>>),
4595}
4596
4597impl<T: AstInfo> AstDisplay for WithOptionValue<T> {
4598    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4599        if f.redacted() {
4600            // When adding branches to this match statement, think about whether it is OK for us to collect
4601            // the value as part of our telemetry. Check the data management policy to be sure!
4602            match self {
4603                WithOptionValue::Value(_)
4604                | WithOptionValue::Sequence(_)
4605                | WithOptionValue::Map(_)
4606                | WithOptionValue::RetainHistoryFor(_)
4607                | WithOptionValue::Refresh(_)
4608                | WithOptionValue::Expr(_) => {
4609                    // These are redact-aware.
4610                }
4611                WithOptionValue::ConnectionKafkaBroker(_) => {
4612                    f.write_str("'<REDACTED>'");
4613                    return;
4614                }
4615                // A secret reference is a catalog item name, not the secret
4616                // value, so it is safe to show in redacted output. An option
4617                // that accepts an inline credential is parsed as a `Value`,
4618                // which is redacted by that arm together with the option's
4619                // `redact_value()`.
4620                WithOptionValue::Secret(_) => {}
4621                WithOptionValue::DataType(_)
4622                | WithOptionValue::Item(_)
4623                | WithOptionValue::UnresolvedItemName(_)
4624                | WithOptionValue::Ident(_)
4625                | WithOptionValue::ConnectionAwsPrivatelink(_)
4626                | WithOptionValue::KafkaMatchingBrokerRule(_)
4627                | WithOptionValue::ClusterReplicas(_)
4628                | WithOptionValue::ClusterScheduleOptionValue(_)
4629                | WithOptionValue::ClusterAutoScalingStrategyOptionValue(_)
4630                | WithOptionValue::ClusterAlterStrategy(_)
4631                | WithOptionValue::NetworkPolicyRules(_) => {
4632                    // These do not need redaction.
4633                }
4634            }
4635        }
4636        match self {
4637            WithOptionValue::Sequence(values) => {
4638                f.write_str("(");
4639                f.write_node(&display::comma_separated(values));
4640                f.write_str(")");
4641            }
4642            WithOptionValue::Map(values) => {
4643                f.write_str("MAP[");
4644                let len = values.len();
4645                for (i, (key, value)) in values.iter().enumerate() {
4646                    f.write_str("'");
4647                    f.write_node(&display::escape_single_quote_string(key));
4648                    f.write_str("' => ");
4649                    f.write_node(value);
4650                    if i + 1 < len {
4651                        f.write_str(", ");
4652                    }
4653                }
4654                f.write_str("]");
4655            }
4656            WithOptionValue::Expr(e) => f.write_node(e),
4657            WithOptionValue::Value(value) => f.write_node(value),
4658            WithOptionValue::DataType(typ) => f.write_node(typ),
4659            WithOptionValue::Secret(name) => {
4660                f.write_str("SECRET ");
4661                f.write_node(name)
4662            }
4663            WithOptionValue::Item(obj) => f.write_node(obj),
4664            WithOptionValue::UnresolvedItemName(r) => f.write_node(r),
4665            WithOptionValue::Ident(r) => f.write_node(r),
4666            WithOptionValue::ClusterReplicas(replicas) => {
4667                f.write_str("(");
4668                f.write_node(&display::comma_separated(replicas));
4669                f.write_str(")");
4670            }
4671            WithOptionValue::NetworkPolicyRules(rules) => {
4672                f.write_str("(");
4673                f.write_node(&display::comma_separated(rules));
4674                f.write_str(")");
4675            }
4676            WithOptionValue::ConnectionAwsPrivatelink(aws_privatelink) => {
4677                f.write_node(aws_privatelink);
4678            }
4679            WithOptionValue::KafkaMatchingBrokerRule(rule) => {
4680                f.write_node(rule);
4681            }
4682            WithOptionValue::ConnectionKafkaBroker(broker) => {
4683                f.write_node(broker);
4684            }
4685            WithOptionValue::RetainHistoryFor(value) => {
4686                f.write_str("FOR ");
4687                f.write_node(value);
4688            }
4689            WithOptionValue::Refresh(opt) => f.write_node(opt),
4690            WithOptionValue::ClusterScheduleOptionValue(value) => f.write_node(value),
4691            WithOptionValue::ClusterAutoScalingStrategyOptionValue(value) => f.write_node(value),
4692            WithOptionValue::ClusterAlterStrategy(value) => f.write_node(value),
4693        }
4694    }
4695}
4696impl_display_t!(WithOptionValue);
4697
4698#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4699pub enum RefreshOptionValue<T: AstInfo> {
4700    OnCommit,
4701    AtCreation,
4702    At(RefreshAtOptionValue<T>),
4703    Every(RefreshEveryOptionValue<T>),
4704}
4705
4706#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4707pub struct RefreshAtOptionValue<T: AstInfo> {
4708    // We need an Expr because we want to support `mz_now()`.
4709    pub time: Expr<T>,
4710}
4711
4712#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4713pub struct RefreshEveryOptionValue<T: AstInfo> {
4714    // The refresh interval.
4715    pub interval: IntervalValue,
4716    // We need an Expr because we want to support `mz_now()`.
4717    pub aligned_to: Option<Expr<T>>,
4718}
4719
4720impl<T: AstInfo> AstDisplay for RefreshOptionValue<T> {
4721    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4722        match self {
4723            RefreshOptionValue::OnCommit => {
4724                f.write_str("ON COMMIT");
4725            }
4726            RefreshOptionValue::AtCreation => {
4727                f.write_str("AT CREATION");
4728            }
4729            RefreshOptionValue::At(RefreshAtOptionValue { time }) => {
4730                f.write_str("AT ");
4731                f.write_node(time);
4732            }
4733            RefreshOptionValue::Every(RefreshEveryOptionValue {
4734                interval,
4735                aligned_to,
4736            }) => {
4737                f.write_str("EVERY '");
4738                f.write_node(interval);
4739                if let Some(aligned_to) = aligned_to {
4740                    f.write_str(" ALIGNED TO ");
4741                    f.write_node(aligned_to)
4742                }
4743            }
4744        }
4745    }
4746}
4747
4748#[derive(
4749    Debug,
4750    Clone,
4751    PartialEq,
4752    Eq,
4753    Hash,
4754    PartialOrd,
4755    Ord,
4756    Deserialize,
4757    Serialize
4758)]
4759pub enum ClusterScheduleOptionValue {
4760    Manual,
4761    Refresh {
4762        hydration_time_estimate: Option<IntervalValue>,
4763    },
4764}
4765
4766impl Default for ClusterScheduleOptionValue {
4767    fn default() -> Self {
4768        // (Has to be consistent with `impl Default for ClusterSchedule`.)
4769        ClusterScheduleOptionValue::Manual
4770    }
4771}
4772
4773impl AstDisplay for ClusterScheduleOptionValue {
4774    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4775        match self {
4776            ClusterScheduleOptionValue::Manual => {
4777                f.write_str("MANUAL");
4778            }
4779            ClusterScheduleOptionValue::Refresh {
4780                hydration_time_estimate,
4781            } => {
4782                f.write_str("ON REFRESH");
4783                if let Some(hydration_time_estimate) = hydration_time_estimate {
4784                    f.write_str(" (HYDRATION TIME ESTIMATE = '");
4785                    f.write_node(hydration_time_estimate);
4786                    f.write_str(")");
4787                }
4788            }
4789        }
4790    }
4791}
4792
4793/// The value of the `AUTO SCALING STRATEGY` cluster option: the autoscaling
4794/// policy block. Extensible: future strategies are additional optional
4795/// sub-policies, so the block grows without changing existing ones. An empty
4796/// block (all sub-policies absent) disables autoscaling for the cluster, the same
4797/// as `RESET (AUTO SCALING STRATEGY)`.
4798#[derive(
4799    Debug,
4800    Clone,
4801    PartialEq,
4802    Eq,
4803    Hash,
4804    PartialOrd,
4805    Ord,
4806    Deserialize,
4807    Serialize
4808)]
4809pub struct ClusterAutoScalingStrategyOptionValue {
4810    pub on_hydration: Option<OnHydrationOptionValue>,
4811}
4812
4813impl AstDisplay for ClusterAutoScalingStrategyOptionValue {
4814    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4815        f.write_str("(");
4816        if let Some(on_hydration) = &self.on_hydration {
4817            f.write_node(on_hydration);
4818        }
4819        f.write_str(")");
4820    }
4821}
4822
4823/// The `ON HYDRATION` autoscaling sub-policy: while objects are un-hydrated, run
4824/// an extra replica at `hydration_size` to accelerate hydration, lingering for
4825/// `linger_duration` after the steady-state replicas hydrate.
4826#[derive(
4827    Debug,
4828    Clone,
4829    PartialEq,
4830    Eq,
4831    Hash,
4832    PartialOrd,
4833    Ord,
4834    Deserialize,
4835    Serialize
4836)]
4837pub struct OnHydrationOptionValue {
4838    pub hydration_size: Value,
4839    pub linger_duration: Option<Value>,
4840}
4841
4842impl AstDisplay for OnHydrationOptionValue {
4843    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4844        f.write_str("ON HYDRATION (HYDRATION SIZE = ");
4845        f.write_node(&self.hydration_size);
4846        if let Some(linger_duration) = &self.linger_duration {
4847            f.write_str(", LINGER DURATION = ");
4848            f.write_node(linger_duration);
4849        }
4850        f.write_str(")");
4851    }
4852}
4853
4854#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4855pub enum TransactionMode {
4856    AccessMode(TransactionAccessMode),
4857    IsolationLevel(TransactionIsolationLevel),
4858}
4859
4860impl AstDisplay for TransactionMode {
4861    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4862        use TransactionMode::*;
4863        match self {
4864            AccessMode(access_mode) => f.write_node(access_mode),
4865            IsolationLevel(iso_level) => {
4866                f.write_str("ISOLATION LEVEL ");
4867                f.write_node(iso_level);
4868            }
4869        }
4870    }
4871}
4872impl_display!(TransactionMode);
4873
4874/// The access mode of a transaction, as specified by the `BEGIN ...` statement.
4875#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4876pub enum TransactionAccessMode {
4877    ReadOnly,
4878    ReadWrite,
4879}
4880
4881impl AstDisplay for TransactionAccessMode {
4882    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4883        use TransactionAccessMode::*;
4884        f.write_str(match self {
4885            ReadOnly => "READ ONLY",
4886            ReadWrite => "READ WRITE",
4887        })
4888    }
4889}
4890impl_display!(TransactionAccessMode);
4891
4892#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
4893pub enum TransactionIsolationLevel {
4894    ReadUncommitted,
4895    ReadCommitted,
4896    RepeatableRead,
4897    Serializable,
4898    StrongSessionSerializable,
4899    StrictSerializable,
4900}
4901
4902impl AstDisplay for TransactionIsolationLevel {
4903    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4904        use TransactionIsolationLevel::*;
4905        f.write_str(match self {
4906            ReadUncommitted => "READ UNCOMMITTED",
4907            ReadCommitted => "READ COMMITTED",
4908            RepeatableRead => "REPEATABLE READ",
4909            Serializable => "SERIALIZABLE",
4910            StrongSessionSerializable => "STRONG SESSION SERIALIZABLE",
4911            StrictSerializable => "STRICT SERIALIZABLE",
4912        })
4913    }
4914}
4915impl_display!(TransactionIsolationLevel);
4916
4917#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4918pub enum SetVariableTo {
4919    Default,
4920    Values(Vec<SetVariableValue>),
4921}
4922
4923impl AstDisplay for SetVariableTo {
4924    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4925        use SetVariableTo::*;
4926        match self {
4927            Values(values) => f.write_node(&display::comma_separated(values)),
4928            Default => f.write_str("DEFAULT"),
4929        }
4930    }
4931}
4932impl_display!(SetVariableTo);
4933
4934#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4935pub enum SetVariableValue {
4936    Ident(Ident),
4937    Literal(Value),
4938}
4939
4940impl AstDisplay for SetVariableValue {
4941    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4942        use SetVariableValue::*;
4943        match self {
4944            Ident(ident) => f.write_node(ident),
4945            Literal(literal) => f.write_node(literal),
4946        }
4947    }
4948}
4949impl_display!(SetVariableValue);
4950
4951impl SetVariableValue {
4952    /// Returns the underlying value without quotes.
4953    pub fn into_unquoted_value(self) -> String {
4954        match self {
4955            // `lit.to_string` will quote a `Value::String`, so get the unquoted
4956            // version.
4957            SetVariableValue::Literal(Value::String(s)) => s,
4958            SetVariableValue::Literal(lit) => lit.to_string(),
4959            SetVariableValue::Ident(ident) => ident.into_string(),
4960        }
4961    }
4962}
4963
4964/// SQL assignment `foo = expr` as used in SQLUpdate
4965#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4966pub struct Assignment<T: AstInfo> {
4967    pub id: Ident,
4968    pub value: Expr<T>,
4969}
4970
4971impl<T: AstInfo> AstDisplay for Assignment<T> {
4972    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4973        f.write_node(&self.id);
4974        f.write_str(" = ");
4975        f.write_node(&self.value);
4976    }
4977}
4978impl_display_t!(Assignment);
4979
4980/// Specifies what [Statement::ExplainPlan] is actually explained.
4981#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4982pub enum ExplainStage {
4983    /// The mz_sql::HirRelationExpr after parsing
4984    RawPlan,
4985    /// The mz_expr::MirRelationExpr after decorrelation
4986    DecorrelatedPlan,
4987    /// The mz_expr::MirRelationExpr after local optimization
4988    LocalPlan,
4989    /// The mz_expr::MirRelationExpr after global optimization
4990    GlobalPlan,
4991    /// The mz_compute_types::plan::Plan
4992    PhysicalPlan,
4993    /// The complete trace of the plan through the optimizer
4994    Trace,
4995    /// Insights about the plan
4996    PlanInsights,
4997}
4998
4999impl ExplainStage {
5000    /// Return the tracing path that corresponds to a given stage.
5001    pub fn paths(&self) -> Option<SmallVec<[NamedPlan; 4]>> {
5002        use NamedPlan::*;
5003        match self {
5004            Self::RawPlan => Some(smallvec![Raw]),
5005            Self::DecorrelatedPlan => Some(smallvec![Decorrelated]),
5006            Self::LocalPlan => Some(smallvec![Local]),
5007            Self::GlobalPlan => Some(smallvec![Global]),
5008            Self::PhysicalPlan => Some(smallvec![Physical]),
5009            Self::Trace => None,
5010            Self::PlanInsights => Some(smallvec![Raw, Global, FastPath]),
5011        }
5012    }
5013
5014    // Whether instead of the plan associated with this [`ExplainStage`] we
5015    // should show the [`NamedPlan::FastPath`] plan if available.
5016    pub fn show_fast_path(&self) -> bool {
5017        match self {
5018            Self::RawPlan => false,
5019            Self::DecorrelatedPlan => false,
5020            Self::LocalPlan => false,
5021            Self::GlobalPlan => true,
5022            Self::PhysicalPlan => true,
5023            Self::Trace => false,
5024            Self::PlanInsights => false,
5025        }
5026    }
5027}
5028
5029impl AstDisplay for ExplainStage {
5030    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5031        match self {
5032            Self::RawPlan => f.write_str("RAW PLAN"),
5033            Self::DecorrelatedPlan => f.write_str("DECORRELATED PLAN"),
5034            Self::LocalPlan => f.write_str("LOCALLY OPTIMIZED PLAN"),
5035            Self::GlobalPlan => f.write_str("OPTIMIZED PLAN"),
5036            Self::PhysicalPlan => f.write_str("PHYSICAL PLAN"),
5037            Self::Trace => f.write_str("OPTIMIZER TRACE"),
5038            Self::PlanInsights => f.write_str("PLAN INSIGHTS"),
5039        }
5040    }
5041}
5042impl_display!(ExplainStage);
5043
5044/// An enum of named plans that identifies specific stages in an optimizer trace
5045/// where these plans can be found.
5046#[derive(Clone)]
5047pub enum NamedPlan {
5048    Raw,
5049    Decorrelated,
5050    Local,
5051    Global,
5052    Physical,
5053    FastPath,
5054}
5055
5056impl NamedPlan {
5057    /// Return the [`NamedPlan`] for a given `path` if it exists.
5058    pub fn of_path(value: &str) -> Option<Self> {
5059        match value {
5060            "optimize/raw" => Some(Self::Raw),
5061            "optimize/hir_to_mir" => Some(Self::Decorrelated),
5062            "optimize/local" => Some(Self::Local),
5063            "optimize/global" => Some(Self::Global),
5064            "optimize/finalize_dataflow" => Some(Self::Physical),
5065            "optimize/fast_path" => Some(Self::FastPath),
5066            _ => None,
5067        }
5068    }
5069
5070    /// Return the tracing path under which the plan can be found in an
5071    /// optimizer trace.
5072    pub fn path(&self) -> &'static str {
5073        match self {
5074            Self::Raw => "optimize/raw",
5075            Self::Decorrelated => "optimize/hir_to_mir",
5076            Self::Local => "optimize/local",
5077            Self::Global => "optimize/global",
5078            Self::Physical => "optimize/finalize_dataflow",
5079            Self::FastPath => "optimize/fast_path",
5080        }
5081    }
5082}
5083
5084/// What is being explained.
5085/// The bools mean whether this is an EXPLAIN BROKEN.
5086#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5087pub enum Explainee<T: AstInfo> {
5088    View(T::ItemName),
5089    MaterializedView(T::ItemName),
5090    Index(T::ItemName),
5091    ReplanView(T::ItemName),
5092    ReplanMaterializedView(T::ItemName),
5093    ReplanIndex(T::ItemName),
5094    Select(Box<SelectStatement<T>>, bool),
5095    CreateView(Box<CreateViewStatement<T>>, bool),
5096    CreateMaterializedView(Box<CreateMaterializedViewStatement<T>>, bool),
5097    CreateIndex(Box<CreateIndexStatement<T>>, bool),
5098    Subscribe(Box<SubscribeStatement<T>>, bool),
5099}
5100
5101impl<T: AstInfo> Explainee<T> {
5102    pub fn name(&self) -> Option<&T::ItemName> {
5103        match self {
5104            Self::View(name)
5105            | Self::ReplanView(name)
5106            | Self::MaterializedView(name)
5107            | Self::ReplanMaterializedView(name)
5108            | Self::Index(name)
5109            | Self::ReplanIndex(name) => Some(name),
5110            Self::Select(..)
5111            | Self::CreateView(..)
5112            | Self::CreateMaterializedView(..)
5113            | Self::CreateIndex(..)
5114            | Self::Subscribe(..) => None,
5115        }
5116    }
5117
5118    pub fn is_view(&self) -> bool {
5119        use Explainee::*;
5120        matches!(self, View(_) | ReplanView(_) | CreateView(_, _))
5121    }
5122}
5123
5124impl<T: AstInfo> AstDisplay for Explainee<T> {
5125    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5126        match self {
5127            Self::View(name) => {
5128                f.write_str("VIEW ");
5129                f.write_node(name);
5130            }
5131            Self::MaterializedView(name) => {
5132                f.write_str("MATERIALIZED VIEW ");
5133                f.write_node(name);
5134            }
5135            Self::Index(name) => {
5136                f.write_str("INDEX ");
5137                f.write_node(name);
5138            }
5139            Self::ReplanView(name) => {
5140                f.write_str("REPLAN VIEW ");
5141                f.write_node(name);
5142            }
5143            Self::ReplanMaterializedView(name) => {
5144                f.write_str("REPLAN MATERIALIZED VIEW ");
5145                f.write_node(name);
5146            }
5147            Self::ReplanIndex(name) => {
5148                f.write_str("REPLAN INDEX ");
5149                f.write_node(name);
5150            }
5151            Self::Select(select, broken) => {
5152                if *broken {
5153                    f.write_str("BROKEN ");
5154                }
5155                f.write_node(select);
5156            }
5157            Self::CreateView(statement, broken) => {
5158                if *broken {
5159                    f.write_str("BROKEN ");
5160                }
5161                f.write_node(statement);
5162            }
5163            Self::CreateMaterializedView(statement, broken) => {
5164                if *broken {
5165                    f.write_str("BROKEN ");
5166                }
5167                f.write_node(statement);
5168            }
5169            Self::CreateIndex(statement, broken) => {
5170                if *broken {
5171                    f.write_str("BROKEN ");
5172                }
5173                f.write_node(statement);
5174            }
5175            Self::Subscribe(statement, broken) => {
5176                if *broken {
5177                    f.write_str("BROKEN ");
5178                }
5179                f.write_node(statement);
5180            }
5181        }
5182    }
5183}
5184impl_display_t!(Explainee);
5185
5186#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
5187pub enum ExplainFormat {
5188    /// Human readable display format
5189    Text,
5190    /// Human readable display format with full debug information
5191    VerboseText,
5192    /// Machine-consumable JSON format
5193    Json,
5194    /// Machine-consumable DOT (graphviz) format
5195    Dot,
5196}
5197
5198impl AstDisplay for ExplainFormat {
5199    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5200        match self {
5201            Self::Text => f.write_str("TEXT"),
5202            Self::VerboseText => f.write_str("VERBOSE TEXT"),
5203            Self::Json => f.write_str("JSON"),
5204            Self::Dot => f.write_str("DOT"),
5205        }
5206    }
5207}
5208impl_display!(ExplainFormat);
5209
5210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5211pub enum IfExistsBehavior {
5212    Error,
5213    Skip,
5214    Replace,
5215}
5216
5217/// `DECLARE ...`
5218#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5219pub struct DeclareStatement<T: AstInfo> {
5220    pub name: Ident,
5221    pub stmt: Box<T::NestedStatement>,
5222    pub sql: String,
5223}
5224
5225impl<T: AstInfo> AstDisplay for DeclareStatement<T> {
5226    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5227        f.write_str("DECLARE ");
5228        f.write_node(&self.name);
5229        f.write_str(" CURSOR FOR ");
5230        f.write_node(&self.stmt);
5231    }
5232}
5233impl_display_t!(DeclareStatement);
5234
5235/// `CLOSE ...`
5236#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5237pub struct CloseStatement {
5238    pub name: Ident,
5239}
5240
5241impl AstDisplay for CloseStatement {
5242    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5243        f.write_str("CLOSE ");
5244        f.write_node(&self.name);
5245    }
5246}
5247impl_display!(CloseStatement);
5248
5249#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5250pub enum FetchOptionName {
5251    Timeout,
5252}
5253
5254impl AstDisplay for FetchOptionName {
5255    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5256        f.write_str(match self {
5257            FetchOptionName::Timeout => "TIMEOUT",
5258        })
5259    }
5260}
5261
5262impl WithOptionName for FetchOptionName {
5263    /// # WARNING
5264    ///
5265    /// Whenever implementing this trait consider very carefully whether or not
5266    /// this value could contain sensitive user data. If you're uncertain, err
5267    /// on the conservative side and return `true`.
5268    fn redact_value(&self) -> bool {
5269        match self {
5270            FetchOptionName::Timeout => false,
5271        }
5272    }
5273}
5274
5275#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5276pub struct FetchOption<T: AstInfo> {
5277    pub name: FetchOptionName,
5278    pub value: Option<WithOptionValue<T>>,
5279}
5280impl_display_for_with_option!(FetchOption);
5281
5282/// `FETCH ...`
5283#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5284pub struct FetchStatement<T: AstInfo> {
5285    pub name: Ident,
5286    pub count: Option<FetchDirection>,
5287    pub options: Vec<FetchOption<T>>,
5288}
5289
5290impl<T: AstInfo> AstDisplay for FetchStatement<T> {
5291    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5292        f.write_str("FETCH ");
5293        if let Some(ref count) = self.count {
5294            f.write_str(format!("{} ", count));
5295        }
5296        // `FETCH` consumes an optional leading `FORWARD` keyword, so a cursor
5297        // literally named `forward` printed bare with no preceding count would
5298        // be swallowed on reparse, leaving no cursor name. Force it to quote.
5299        if self.count.is_none() && self.name.as_str().eq_ignore_ascii_case("forward") {
5300            f.write_str(self.name.to_ast_string_stable());
5301        } else {
5302            f.write_node(&self.name);
5303        }
5304        if !self.options.is_empty() {
5305            f.write_str(" WITH (");
5306            f.write_node(&display::comma_separated(&self.options));
5307            f.write_str(")");
5308        }
5309    }
5310}
5311impl_display_t!(FetchStatement);
5312
5313#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5314pub enum FetchDirection {
5315    ForwardAll,
5316    ForwardCount(u64),
5317}
5318
5319impl AstDisplay for FetchDirection {
5320    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5321        match self {
5322            FetchDirection::ForwardAll => f.write_str("ALL"),
5323            FetchDirection::ForwardCount(count) => f.write_str(format!("{}", count)),
5324        }
5325    }
5326}
5327impl_display!(FetchDirection);
5328
5329/// `PREPARE ...`
5330#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5331pub struct PrepareStatement<T: AstInfo> {
5332    pub name: Ident,
5333    pub stmt: Box<T::NestedStatement>,
5334    pub sql: String,
5335}
5336
5337impl<T: AstInfo> AstDisplay for PrepareStatement<T> {
5338    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5339        f.write_str("PREPARE ");
5340        f.write_node(&self.name);
5341        f.write_str(" AS ");
5342        f.write_node(&self.stmt);
5343    }
5344}
5345impl_display_t!(PrepareStatement);
5346
5347/// `EXECUTE ...`
5348#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5349pub struct ExecuteStatement<T: AstInfo> {
5350    pub name: Ident,
5351    pub params: Vec<Expr<T>>,
5352}
5353
5354impl<T: AstInfo> AstDisplay for ExecuteStatement<T> {
5355    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5356        f.write_str("EXECUTE ");
5357        f.write_node(&self.name);
5358        if !self.params.is_empty() {
5359            f.write_str(" (");
5360            f.write_node(&display::comma_separated(&self.params));
5361            f.write_str(")");
5362        }
5363    }
5364}
5365impl_display_t!(ExecuteStatement);
5366
5367/// `EXECUTE UNIT TEST ...`
5368#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5369pub struct ExecuteUnitTestStatement<T: AstInfo> {
5370    pub name: Ident,
5371    pub target: T::ItemName,
5372    pub at_time: Option<Expr<T>>,
5373    pub mocks: Vec<MockViewDef<T>>,
5374    pub expected: ExpectedResultDef<T>,
5375}
5376
5377impl<T: AstInfo> AstDisplay for ExecuteUnitTestStatement<T> {
5378    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5379        f.write_str("EXECUTE UNIT TEST ");
5380        f.write_node(&self.name);
5381        f.write_str(" FOR ");
5382        f.write_node(&self.target);
5383        if let Some(at_time) = &self.at_time {
5384            f.write_str(" AT TIME ");
5385            f.write_node(at_time);
5386        }
5387        for (i, mock) in self.mocks.iter().enumerate() {
5388            f.write_str(if i == 0 { " MOCK " } else { ", MOCK " });
5389            f.write_node(mock);
5390        }
5391        f.write_str(" EXPECTED ");
5392        f.write_node(&self.expected);
5393    }
5394}
5395impl_display_t!(ExecuteUnitTestStatement);
5396
5397/// Mock view definition for EXECUTE UNIT TEST
5398#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5399pub struct MockViewDef<T: AstInfo> {
5400    pub name: T::ItemName,
5401    pub columns: Vec<ColumnDef<T>>,
5402    pub query: Query<T>,
5403}
5404
5405impl<T: AstInfo> AstDisplay for MockViewDef<T> {
5406    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5407        f.write_node(&self.name);
5408        f.write_str("(");
5409        f.write_node(&display::comma_separated(&self.columns));
5410        f.write_str(") AS (");
5411        f.write_node(&self.query);
5412        f.write_str(")");
5413    }
5414}
5415impl_display_t!(MockViewDef);
5416
5417/// Expected result definition for EXECUTE UNIT TEST
5418#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5419pub struct ExpectedResultDef<T: AstInfo> {
5420    pub columns: Vec<ColumnDef<T>>,
5421    pub query: Query<T>,
5422}
5423
5424impl<T: AstInfo> AstDisplay for ExpectedResultDef<T> {
5425    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5426        f.write_str("(");
5427        f.write_node(&display::comma_separated(&self.columns));
5428        f.write_str(") AS (");
5429        f.write_node(&self.query);
5430        f.write_str(")");
5431    }
5432}
5433impl_display_t!(ExpectedResultDef);
5434
5435/// `DEALLOCATE ...`
5436#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5437pub struct DeallocateStatement {
5438    pub name: Option<Ident>,
5439}
5440
5441impl AstDisplay for DeallocateStatement {
5442    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5443        f.write_str("DEALLOCATE ");
5444        match &self.name {
5445            Some(name) => f.write_node(name),
5446            None => f.write_str("ALL"),
5447        };
5448    }
5449}
5450impl_display!(DeallocateStatement);
5451
5452/// `RAISE ...`
5453#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5454pub struct RaiseStatement {
5455    pub severity: NoticeSeverity,
5456}
5457
5458impl AstDisplay for RaiseStatement {
5459    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5460        f.write_str("RAISE ");
5461        f.write_node(&self.severity);
5462    }
5463}
5464impl_display!(RaiseStatement);
5465
5466#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5467pub enum NoticeSeverity {
5468    Debug,
5469    Info,
5470    Log,
5471    Notice,
5472    Warning,
5473}
5474
5475impl AstDisplay for NoticeSeverity {
5476    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5477        f.write_str(match self {
5478            NoticeSeverity::Debug => "DEBUG",
5479            NoticeSeverity::Info => "INFO",
5480            NoticeSeverity::Log => "LOG",
5481            NoticeSeverity::Notice => "NOTICE",
5482            NoticeSeverity::Warning => "WARNING",
5483        })
5484    }
5485}
5486impl_display!(NoticeSeverity);
5487
5488/// `ALTER SYSTEM SET ...`
5489#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5490pub struct AlterSystemSetStatement {
5491    pub name: Ident,
5492    pub to: SetVariableTo,
5493}
5494
5495impl AstDisplay for AlterSystemSetStatement {
5496    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5497        f.write_str("ALTER SYSTEM SET ");
5498        f.write_node(&self.name);
5499        f.write_str(" = ");
5500        f.write_node(&self.to);
5501    }
5502}
5503impl_display!(AlterSystemSetStatement);
5504
5505/// `ALTER SYSTEM RESET ...`
5506#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5507pub struct AlterSystemResetStatement {
5508    pub name: Ident,
5509}
5510
5511impl AstDisplay for AlterSystemResetStatement {
5512    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5513        f.write_str("ALTER SYSTEM RESET ");
5514        f.write_node(&self.name);
5515    }
5516}
5517impl_display!(AlterSystemResetStatement);
5518
5519/// `ALTER SYSTEM RESET ALL`
5520#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5521pub struct AlterSystemResetAllStatement {}
5522
5523impl AstDisplay for AlterSystemResetAllStatement {
5524    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5525        f.write_str("ALTER SYSTEM RESET ALL");
5526    }
5527}
5528impl_display!(AlterSystemResetAllStatement);
5529
5530#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5531pub enum AsOf<T: AstInfo> {
5532    At(Expr<T>),
5533    AtLeast(Expr<T>),
5534}
5535
5536impl<T: AstInfo> AstDisplay for AsOf<T> {
5537    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5538        f.write_str("AS OF ");
5539        match self {
5540            AsOf::At(expr) => f.write_node(expr),
5541            AsOf::AtLeast(expr) => {
5542                f.write_str("AT LEAST ");
5543                f.write_node(expr);
5544            }
5545        }
5546    }
5547}
5548impl_display_t!(AsOf);
5549
5550#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
5551pub enum ShowStatement<T: AstInfo> {
5552    ShowObjects(ShowObjectsStatement<T>),
5553    ShowColumns(ShowColumnsStatement<T>),
5554    ShowCreateView(ShowCreateViewStatement<T>),
5555    ShowCreateMaterializedView(ShowCreateMaterializedViewStatement<T>),
5556    ShowCreateSource(ShowCreateSourceStatement<T>),
5557    ShowCreateTable(ShowCreateTableStatement<T>),
5558    ShowCreateSink(ShowCreateSinkStatement<T>),
5559    ShowCreateMetricSink(ShowCreateMetricSinkStatement<T>),
5560    ShowCreateIndex(ShowCreateIndexStatement<T>),
5561    ShowCreateConnection(ShowCreateConnectionStatement<T>),
5562    ShowCreateCluster(ShowCreateClusterStatement<T>),
5563    ShowCreateType(ShowCreateTypeStatement<T>),
5564    ShowVariable(ShowVariableStatement),
5565    InspectShard(InspectShardStatement),
5566}
5567
5568impl<T: AstInfo> AstDisplay for ShowStatement<T> {
5569    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5570        match self {
5571            ShowStatement::ShowObjects(stmt) => f.write_node(stmt),
5572            ShowStatement::ShowColumns(stmt) => f.write_node(stmt),
5573            ShowStatement::ShowCreateView(stmt) => f.write_node(stmt),
5574            ShowStatement::ShowCreateMaterializedView(stmt) => f.write_node(stmt),
5575            ShowStatement::ShowCreateSource(stmt) => f.write_node(stmt),
5576            ShowStatement::ShowCreateTable(stmt) => f.write_node(stmt),
5577            ShowStatement::ShowCreateSink(stmt) => f.write_node(stmt),
5578            ShowStatement::ShowCreateMetricSink(stmt) => f.write_node(stmt),
5579            ShowStatement::ShowCreateIndex(stmt) => f.write_node(stmt),
5580            ShowStatement::ShowCreateConnection(stmt) => f.write_node(stmt),
5581            ShowStatement::ShowCreateCluster(stmt) => f.write_node(stmt),
5582            ShowStatement::ShowCreateType(stmt) => f.write_node(stmt),
5583            ShowStatement::ShowVariable(stmt) => f.write_node(stmt),
5584            ShowStatement::InspectShard(stmt) => f.write_node(stmt),
5585        }
5586    }
5587}
5588impl_display_t!(ShowStatement);
5589
5590/// `GRANT ...`
5591#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5592pub struct GrantRoleStatement<T: AstInfo> {
5593    /// The roles that are gaining members.
5594    pub role_names: Vec<T::RoleName>,
5595    /// The roles that will be added to `role_name`.
5596    pub member_names: Vec<T::RoleName>,
5597}
5598
5599impl<T: AstInfo> AstDisplay for GrantRoleStatement<T> {
5600    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5601        f.write_str("GRANT ");
5602        f.write_node(&display::comma_separated(&self.role_names));
5603        f.write_str(" TO ");
5604        f.write_node(&display::comma_separated(&self.member_names));
5605    }
5606}
5607impl_display_t!(GrantRoleStatement);
5608
5609/// `REVOKE ...`
5610#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5611pub struct RevokeRoleStatement<T: AstInfo> {
5612    /// The roles that are losing members.
5613    pub role_names: Vec<T::RoleName>,
5614    /// The roles that will be removed from `role_name`.
5615    pub member_names: Vec<T::RoleName>,
5616}
5617
5618impl<T: AstInfo> AstDisplay for RevokeRoleStatement<T> {
5619    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5620        f.write_str("REVOKE ");
5621        f.write_node(&display::comma_separated(&self.role_names));
5622        f.write_str(" FROM ");
5623        f.write_node(&display::comma_separated(&self.member_names));
5624    }
5625}
5626impl_display_t!(RevokeRoleStatement);
5627
5628#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5629pub enum Privilege {
5630    SELECT,
5631    INSERT,
5632    UPDATE,
5633    DELETE,
5634    USAGE,
5635    CREATE,
5636    CREATEROLE,
5637    CREATEDB,
5638    CREATECLUSTER,
5639    CREATENETWORKPOLICY,
5640}
5641
5642impl AstDisplay for Privilege {
5643    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5644        f.write_str(match self {
5645            Privilege::SELECT => "SELECT",
5646            Privilege::INSERT => "INSERT",
5647            Privilege::UPDATE => "UPDATE",
5648            Privilege::DELETE => "DELETE",
5649            Privilege::CREATE => "CREATE",
5650            Privilege::USAGE => "USAGE",
5651            Privilege::CREATEROLE => "CREATEROLE",
5652            Privilege::CREATEDB => "CREATEDB",
5653            Privilege::CREATECLUSTER => "CREATECLUSTER",
5654            Privilege::CREATENETWORKPOLICY => "CREATENETWORKPOLICY",
5655        });
5656    }
5657}
5658impl_display!(Privilege);
5659
5660#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5661pub enum PrivilegeSpecification {
5662    All,
5663    Privileges(Vec<Privilege>),
5664}
5665
5666impl AstDisplay for PrivilegeSpecification {
5667    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5668        match self {
5669            PrivilegeSpecification::All => f.write_str("ALL"),
5670            PrivilegeSpecification::Privileges(privileges) => {
5671                f.write_node(&display::comma_separated(privileges))
5672            }
5673        }
5674    }
5675}
5676impl_display!(PrivilegeSpecification);
5677
5678#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5679pub enum GrantTargetSpecification<T: AstInfo> {
5680    Object {
5681        /// The type of object.
5682        ///
5683        /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5684        object_type: ObjectType,
5685        /// Specification of each object affected.
5686        object_spec_inner: GrantTargetSpecificationInner<T>,
5687    },
5688    System,
5689}
5690
5691#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5692pub enum GrantTargetSpecificationInner<T: AstInfo> {
5693    All(GrantTargetAllSpecification<T>),
5694    Objects { names: Vec<T::ObjectName> },
5695}
5696
5697#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5698pub enum GrantTargetAllSpecification<T: AstInfo> {
5699    All,
5700    AllDatabases { databases: Vec<T::DatabaseName> },
5701    AllSchemas { schemas: Vec<T::SchemaName> },
5702}
5703
5704impl<T: AstInfo> GrantTargetAllSpecification<T> {
5705    pub fn len(&self) -> usize {
5706        match self {
5707            GrantTargetAllSpecification::All => 1,
5708            GrantTargetAllSpecification::AllDatabases { databases } => databases.len(),
5709            GrantTargetAllSpecification::AllSchemas { schemas } => schemas.len(),
5710        }
5711    }
5712}
5713
5714/// Writes the plural keyword for `object_type` as `GRANT`/`REVOKE ... ON ALL`
5715/// expects it. Most object types just take a trailing `S` (`TABLES`, `SECRETS`,
5716/// ...), but `NETWORK POLICY` pluralizes to the `POLICIES` keyword the parser
5717/// accepts — naively appending `S` would emit `NETWORK POLICYS`, which fails to
5718/// reparse.
5719fn write_grant_object_type_plural<W: fmt::Write>(
5720    f: &mut AstFormatter<W>,
5721    object_type: &ObjectType,
5722) {
5723    match object_type {
5724        ObjectType::NetworkPolicy => f.write_str("POLICIES"),
5725        other => {
5726            f.write_node(other);
5727            f.write_str("S");
5728        }
5729    }
5730}
5731
5732impl<T: AstInfo> AstDisplay for GrantTargetSpecification<T> {
5733    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5734        match self {
5735            GrantTargetSpecification::Object {
5736                object_type,
5737                object_spec_inner,
5738            } => match object_spec_inner {
5739                GrantTargetSpecificationInner::All(all_spec) => match all_spec {
5740                    GrantTargetAllSpecification::All => {
5741                        f.write_str("ALL ");
5742                        write_grant_object_type_plural(f, object_type);
5743                    }
5744                    GrantTargetAllSpecification::AllDatabases { databases } => {
5745                        f.write_str("ALL ");
5746                        write_grant_object_type_plural(f, object_type);
5747                        f.write_str(" IN DATABASE ");
5748                        f.write_node(&display::comma_separated(databases));
5749                    }
5750                    GrantTargetAllSpecification::AllSchemas { schemas } => {
5751                        f.write_str("ALL ");
5752                        write_grant_object_type_plural(f, object_type);
5753                        f.write_str(" IN SCHEMA ");
5754                        f.write_node(&display::comma_separated(schemas));
5755                    }
5756                },
5757                GrantTargetSpecificationInner::Objects { names } => {
5758                    f.write_node(object_type);
5759                    f.write_str(" ");
5760                    f.write_node(&display::comma_separated(names));
5761                }
5762            },
5763            GrantTargetSpecification::System => f.write_str("SYSTEM"),
5764        }
5765    }
5766}
5767impl_display_t!(GrantTargetSpecification);
5768
5769/// `GRANT ...`
5770#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5771pub struct GrantPrivilegesStatement<T: AstInfo> {
5772    /// The privileges being granted on an object.
5773    pub privileges: PrivilegeSpecification,
5774    /// The objects that are affected by the GRANT.
5775    pub target: GrantTargetSpecification<T>,
5776    /// The roles that will granted the privileges.
5777    pub roles: Vec<T::RoleName>,
5778}
5779
5780impl<T: AstInfo> AstDisplay for GrantPrivilegesStatement<T> {
5781    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5782        f.write_str("GRANT ");
5783        f.write_node(&self.privileges);
5784        f.write_str(" ON ");
5785        f.write_node(&self.target);
5786        f.write_str(" TO ");
5787        f.write_node(&display::comma_separated(&self.roles));
5788    }
5789}
5790impl_display_t!(GrantPrivilegesStatement);
5791
5792/// `REVOKE ...`
5793#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5794pub struct RevokePrivilegesStatement<T: AstInfo> {
5795    /// The privileges being revoked.
5796    pub privileges: PrivilegeSpecification,
5797    /// The objects that are affected by the REVOKE.
5798    pub target: GrantTargetSpecification<T>,
5799    /// The roles that will have privileges revoked.
5800    pub roles: Vec<T::RoleName>,
5801}
5802
5803impl<T: AstInfo> AstDisplay for RevokePrivilegesStatement<T> {
5804    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5805        f.write_str("REVOKE ");
5806        f.write_node(&self.privileges);
5807        f.write_str(" ON ");
5808        f.write_node(&self.target);
5809        f.write_str(" FROM ");
5810        f.write_node(&display::comma_separated(&self.roles));
5811    }
5812}
5813impl_display_t!(RevokePrivilegesStatement);
5814
5815#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5816pub enum TargetRoleSpecification<T: AstInfo> {
5817    /// Specific list of roles.
5818    Roles(Vec<T::RoleName>),
5819    /// All current and future roles.
5820    AllRoles,
5821}
5822
5823impl<T: AstInfo> AstDisplay for TargetRoleSpecification<T> {
5824    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5825        match self {
5826            TargetRoleSpecification::Roles(roles) => f.write_node(&display::comma_separated(roles)),
5827            TargetRoleSpecification::AllRoles => f.write_str("ALL ROLES"),
5828        }
5829    }
5830}
5831impl_display_t!(TargetRoleSpecification);
5832
5833#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5834pub struct AbbreviatedGrantStatement<T: AstInfo> {
5835    /// The privileges being granted.
5836    pub privileges: PrivilegeSpecification,
5837    /// The type of object.
5838    ///
5839    /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5840    pub object_type: ObjectType,
5841    /// The roles that will granted the privileges.
5842    pub grantees: Vec<T::RoleName>,
5843}
5844
5845impl<T: AstInfo> AstDisplay for AbbreviatedGrantStatement<T> {
5846    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5847        f.write_str("GRANT ");
5848        f.write_node(&self.privileges);
5849        f.write_str(" ON ");
5850        write_grant_object_type_plural(f, &self.object_type);
5851        f.write_str(" TO ");
5852        f.write_node(&display::comma_separated(&self.grantees));
5853    }
5854}
5855impl_display_t!(AbbreviatedGrantStatement);
5856
5857#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5858pub struct AbbreviatedRevokeStatement<T: AstInfo> {
5859    /// The privileges being revoked.
5860    pub privileges: PrivilegeSpecification,
5861    /// The type of object.
5862    ///
5863    /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5864    pub object_type: ObjectType,
5865    /// The roles that the privilege will be revoked from.
5866    pub revokees: Vec<T::RoleName>,
5867}
5868
5869impl<T: AstInfo> AstDisplay for AbbreviatedRevokeStatement<T> {
5870    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5871        f.write_str("REVOKE ");
5872        f.write_node(&self.privileges);
5873        f.write_str(" ON ");
5874        write_grant_object_type_plural(f, &self.object_type);
5875        f.write_str(" FROM ");
5876        f.write_node(&display::comma_separated(&self.revokees));
5877    }
5878}
5879impl_display_t!(AbbreviatedRevokeStatement);
5880
5881#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5882pub enum AbbreviatedGrantOrRevokeStatement<T: AstInfo> {
5883    Grant(AbbreviatedGrantStatement<T>),
5884    Revoke(AbbreviatedRevokeStatement<T>),
5885}
5886
5887impl<T: AstInfo> AbbreviatedGrantOrRevokeStatement<T> {
5888    pub fn privileges(&self) -> &PrivilegeSpecification {
5889        match self {
5890            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.privileges,
5891            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.privileges,
5892        }
5893    }
5894
5895    pub fn object_type(&self) -> &ObjectType {
5896        match self {
5897            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.object_type,
5898            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.object_type,
5899        }
5900    }
5901
5902    pub fn roles(&self) -> &Vec<T::RoleName> {
5903        match self {
5904            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.grantees,
5905            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.revokees,
5906        }
5907    }
5908}
5909
5910impl<T: AstInfo> AstDisplay for AbbreviatedGrantOrRevokeStatement<T> {
5911    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5912        match self {
5913            AbbreviatedGrantOrRevokeStatement::Grant(grant) => f.write_node(grant),
5914            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => f.write_node(revoke),
5915        }
5916    }
5917}
5918impl_display_t!(AbbreviatedGrantOrRevokeStatement);
5919
5920/// `ALTER DEFAULT PRIVILEGES ...`
5921#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5922pub struct AlterDefaultPrivilegesStatement<T: AstInfo> {
5923    /// The roles for which created objects are affected.
5924    pub target_roles: TargetRoleSpecification<T>,
5925    /// The objects that are affected by the default privilege.
5926    pub target_objects: GrantTargetAllSpecification<T>,
5927    /// The privilege to grant or revoke.
5928    pub grant_or_revoke: AbbreviatedGrantOrRevokeStatement<T>,
5929}
5930
5931impl<T: AstInfo> AstDisplay for AlterDefaultPrivilegesStatement<T> {
5932    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5933        f.write_str("ALTER DEFAULT PRIVILEGES");
5934        match &self.target_roles {
5935            TargetRoleSpecification::Roles(_) => {
5936                f.write_str(" FOR ROLE ");
5937                f.write_node(&self.target_roles);
5938            }
5939            TargetRoleSpecification::AllRoles => {
5940                f.write_str(" FOR ");
5941                f.write_node(&self.target_roles);
5942            }
5943        }
5944        match &self.target_objects {
5945            GrantTargetAllSpecification::All => {}
5946            GrantTargetAllSpecification::AllDatabases { databases } => {
5947                f.write_str(" IN DATABASE ");
5948                f.write_node(&display::comma_separated(databases));
5949            }
5950            GrantTargetAllSpecification::AllSchemas { schemas } => {
5951                f.write_str(" IN SCHEMA ");
5952                f.write_node(&display::comma_separated(schemas));
5953            }
5954        }
5955        f.write_str(" ");
5956        f.write_node(&self.grant_or_revoke);
5957    }
5958}
5959impl_display_t!(AlterDefaultPrivilegesStatement);
5960
5961/// `REASSIGN OWNED ...`
5962#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5963pub struct ReassignOwnedStatement<T: AstInfo> {
5964    /// The roles whose owned objects are being reassigned.
5965    pub old_roles: Vec<T::RoleName>,
5966    /// The new owner of the objects.
5967    pub new_role: T::RoleName,
5968}
5969
5970impl<T: AstInfo> AstDisplay for ReassignOwnedStatement<T> {
5971    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5972        f.write_str("REASSIGN OWNED BY ");
5973        f.write_node(&display::comma_separated(&self.old_roles));
5974        f.write_str(" TO ");
5975        f.write_node(&self.new_role);
5976    }
5977}
5978impl_display_t!(ReassignOwnedStatement);
5979
5980/// `COMMENT ON ...`
5981#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5982pub struct CommentStatement<T: AstInfo> {
5983    pub object: CommentObjectType<T>,
5984    pub comment: Option<String>,
5985}
5986
5987impl<T: AstInfo> AstDisplay for CommentStatement<T> {
5988    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5989        f.write_str("COMMENT ON ");
5990        f.write_node(&self.object);
5991
5992        f.write_str(" IS ");
5993        match &self.comment {
5994            Some(s) => {
5995                if f.redacted() {
5996                    // The comment body is arbitrary free text and may contain PII,
5997                    // so redact it like every other user-supplied value.
5998                    f.write_str("'<REDACTED>'");
5999                } else {
6000                    f.write_str("'");
6001                    f.write_node(&display::escape_single_quote_string(s));
6002                    f.write_str("'");
6003                }
6004            }
6005            None => f.write_str("NULL"),
6006        }
6007    }
6008}
6009impl_display_t!(CommentStatement);
6010
6011#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
6012pub struct ColumnName<T: AstInfo> {
6013    pub relation: T::ItemName,
6014    pub column: T::ColumnReference,
6015}
6016
6017impl<T: AstInfo> AstDisplay for ColumnName<T> {
6018    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
6019        f.write_node(&self.relation);
6020        f.write_str(".");
6021        f.write_node(&self.column);
6022    }
6023}
6024impl_display_t!(ColumnName);
6025
6026#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6027pub enum CommentObjectType<T: AstInfo> {
6028    Table { name: T::ItemName },
6029    View { name: T::ItemName },
6030    Column { name: ColumnName<T> },
6031    MaterializedView { name: T::ItemName },
6032    Source { name: T::ItemName },
6033    Sink { name: T::ItemName },
6034    Index { name: T::ItemName },
6035    Func { name: T::ItemName },
6036    Connection { name: T::ItemName },
6037    Type { ty: T::DataType },
6038    Secret { name: T::ItemName },
6039    Role { name: T::RoleName },
6040    Database { name: T::DatabaseName },
6041    Schema { name: T::SchemaName },
6042    Cluster { name: T::ClusterName },
6043    ClusterReplica { name: QualifiedReplica },
6044    NetworkPolicy { name: T::NetworkPolicyName },
6045}
6046
6047impl<T: AstInfo> AstDisplay for CommentObjectType<T> {
6048    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
6049        use CommentObjectType::*;
6050
6051        match self {
6052            Table { name } => {
6053                f.write_str("TABLE ");
6054                f.write_node(name);
6055            }
6056            View { name } => {
6057                f.write_str("VIEW ");
6058                f.write_node(name);
6059            }
6060            Column { name } => {
6061                f.write_str("COLUMN ");
6062                f.write_node(name);
6063            }
6064            MaterializedView { name } => {
6065                f.write_str("MATERIALIZED VIEW ");
6066                f.write_node(name);
6067            }
6068            Source { name } => {
6069                f.write_str("SOURCE ");
6070                f.write_node(name);
6071            }
6072            Sink { name } => {
6073                f.write_str("SINK ");
6074                f.write_node(name);
6075            }
6076            Index { name } => {
6077                f.write_str("INDEX ");
6078                f.write_node(name);
6079            }
6080            Func { name } => {
6081                f.write_str("FUNCTION ");
6082                f.write_node(name);
6083            }
6084            Connection { name } => {
6085                f.write_str("CONNECTION ");
6086                f.write_node(name);
6087            }
6088            Type { ty } => {
6089                f.write_str("TYPE ");
6090                f.write_node(ty);
6091            }
6092            Secret { name } => {
6093                f.write_str("SECRET ");
6094                f.write_node(name);
6095            }
6096            Role { name } => {
6097                f.write_str("ROLE ");
6098                f.write_node(name);
6099            }
6100            Database { name } => {
6101                f.write_str("DATABASE ");
6102                f.write_node(name);
6103            }
6104            Schema { name } => {
6105                f.write_str("SCHEMA ");
6106                f.write_node(name);
6107            }
6108            Cluster { name } => {
6109                f.write_str("CLUSTER ");
6110                f.write_node(name);
6111            }
6112            ClusterReplica { name } => {
6113                f.write_str("CLUSTER REPLICA ");
6114                f.write_node(name);
6115            }
6116            NetworkPolicy { name } => {
6117                f.write_str("NETWORK POLICY ");
6118                f.write_node(name);
6119            }
6120        }
6121    }
6122}
6123
6124impl_display_t!(CommentObjectType);
6125
6126// Include the `AstDisplay` implementations for simple options derived by the
6127// crate's build.rs script.
6128include!(concat!(env!("OUT_DIR"), "/display.simple_options.rs"));