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