Skip to main content

mz_sql_parser/ast/defs/
statement.rs

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