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