Skip to main content

mz_sql_parser/ast/defs/
statement.rs

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