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