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 `AVAILABILITY ZONES [[=] '[' <values> ']' ]` option.
2251    AvailabilityZones,
2252    /// The `DISK` option.
2253    Disk,
2254    /// The `INTROSPECTION INTERVAL [[=] <interval>]` option.
2255    IntrospectionInterval,
2256    /// The `INTROSPECTION DEBUGGING [[=] <enabled>]` option.
2257    IntrospectionDebugging,
2258    /// The `MANAGED` option.
2259    Managed,
2260    /// The `REPLICAS` option.
2261    Replicas,
2262    /// The `REPLICATION FACTOR` option.
2263    ReplicationFactor,
2264    /// The `SIZE` option.
2265    Size,
2266    /// The `SCHEDULE` option.
2267    Schedule,
2268    /// The `WORKLOAD CLASS` option.
2269    WorkloadClass,
2270}
2271
2272impl AstDisplay for ClusterOptionName {
2273    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2274        match self {
2275            ClusterOptionName::AvailabilityZones => f.write_str("AVAILABILITY ZONES"),
2276            ClusterOptionName::Disk => f.write_str("DISK"),
2277            ClusterOptionName::IntrospectionDebugging => f.write_str("INTROSPECTION DEBUGGING"),
2278            ClusterOptionName::IntrospectionInterval => f.write_str("INTROSPECTION INTERVAL"),
2279            ClusterOptionName::Managed => f.write_str("MANAGED"),
2280            ClusterOptionName::Replicas => f.write_str("REPLICAS"),
2281            ClusterOptionName::ReplicationFactor => f.write_str("REPLICATION FACTOR"),
2282            ClusterOptionName::Size => f.write_str("SIZE"),
2283            ClusterOptionName::Schedule => f.write_str("SCHEDULE"),
2284            ClusterOptionName::WorkloadClass => f.write_str("WORKLOAD CLASS"),
2285        }
2286    }
2287}
2288
2289impl WithOptionName for ClusterOptionName {
2290    /// # WARNING
2291    ///
2292    /// Whenever implementing this trait consider very carefully whether or not
2293    /// this value could contain sensitive user data. If you're uncertain, err
2294    /// on the conservative side and return `true`.
2295    fn redact_value(&self) -> bool {
2296        match self {
2297            ClusterOptionName::AvailabilityZones
2298            | ClusterOptionName::Disk
2299            | ClusterOptionName::IntrospectionDebugging
2300            | ClusterOptionName::IntrospectionInterval
2301            | ClusterOptionName::Managed
2302            | ClusterOptionName::Replicas
2303            | ClusterOptionName::ReplicationFactor
2304            | ClusterOptionName::Size
2305            | ClusterOptionName::Schedule
2306            | ClusterOptionName::WorkloadClass => false,
2307        }
2308    }
2309}
2310
2311#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2312/// An option in a `CREATE CLUSTER` statement.
2313pub struct ClusterOption<T: AstInfo> {
2314    pub name: ClusterOptionName,
2315    pub value: Option<WithOptionValue<T>>,
2316}
2317impl_display_for_with_option!(ClusterOption);
2318
2319#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2320pub enum ClusterAlterUntilReadyOptionName {
2321    Timeout,
2322    OnTimeout,
2323}
2324
2325impl AstDisplay for ClusterAlterUntilReadyOptionName {
2326    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2327        match self {
2328            Self::Timeout => f.write_str("TIMEOUT"),
2329            Self::OnTimeout => f.write_str("ON TIMEOUT"),
2330        }
2331    }
2332}
2333
2334impl WithOptionName for ClusterAlterUntilReadyOptionName {
2335    /// # WARNING
2336    ///
2337    /// Whenever implementing this trait consider very carefully whether or not
2338    /// this value could contain sensitive user data. If you're uncertain, err
2339    /// on the conservative side and return `true`.
2340    fn redact_value(&self) -> bool {
2341        match self {
2342            ClusterAlterUntilReadyOptionName::Timeout
2343            | ClusterAlterUntilReadyOptionName::OnTimeout => false,
2344        }
2345    }
2346}
2347
2348#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2349pub struct ClusterAlterUntilReadyOption<T: AstInfo> {
2350    pub name: ClusterAlterUntilReadyOptionName,
2351    pub value: Option<WithOptionValue<T>>,
2352}
2353impl_display_for_with_option!(ClusterAlterUntilReadyOption);
2354
2355#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2356pub enum ClusterAlterOptionName {
2357    Wait,
2358}
2359
2360impl AstDisplay for ClusterAlterOptionName {
2361    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2362        match self {
2363            ClusterAlterOptionName::Wait => f.write_str("WAIT"),
2364        }
2365    }
2366}
2367
2368impl WithOptionName for ClusterAlterOptionName {
2369    /// # WARNING
2370    ///
2371    /// Whenever implementing this trait consider very carefully whether or not
2372    /// this value could contain sensitive user data. If you're uncertain, err
2373    /// on the conservative side and return `true`.
2374    fn redact_value(&self) -> bool {
2375        match self {
2376            ClusterAlterOptionName::Wait => false,
2377        }
2378    }
2379}
2380
2381#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2382pub enum ClusterAlterOptionValue<T: AstInfo> {
2383    For(Value),
2384    UntilReady(Vec<ClusterAlterUntilReadyOption<T>>),
2385}
2386
2387impl<T: AstInfo> AstDisplay for ClusterAlterOptionValue<T> {
2388    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2389        match self {
2390            ClusterAlterOptionValue::For(duration) => {
2391                f.write_str("FOR ");
2392                f.write_node(duration);
2393            }
2394            ClusterAlterOptionValue::UntilReady(options) => {
2395                f.write_str("UNTIL READY (");
2396                f.write_node(&display::comma_separated(options));
2397                f.write_str(")");
2398            }
2399        }
2400    }
2401}
2402
2403#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2404/// An option in a `ALTER CLUSTER... WITH` statement.
2405pub struct ClusterAlterOption<T: AstInfo> {
2406    pub name: ClusterAlterOptionName,
2407    pub value: Option<WithOptionValue<T>>,
2408}
2409
2410impl_display_for_with_option!(ClusterAlterOption);
2411
2412// Note: the `AstDisplay` implementation and `Parser::parse_` method for this
2413// enum are generated automatically by this crate's `build.rs`.
2414#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2415pub enum ClusterFeatureName {
2416    ReoptimizeImportedViews,
2417    EnableNewOuterJoinLowering,
2418    EnableEagerDeltaJoins,
2419    EnableVariadicLeftJoinLowering,
2420    EnableLetrecFixpointAnalysis,
2421    EnableJoinPrioritizeArranged,
2422    EnableProjectionPushdownAfterRelationCse,
2423}
2424
2425impl WithOptionName for ClusterFeatureName {
2426    /// # WARNING
2427    ///
2428    /// Whenever implementing this trait consider very carefully whether or not
2429    /// this value could contain sensitive user data. If you're uncertain, err
2430    /// on the conservative side and return `true`.
2431    fn redact_value(&self) -> bool {
2432        match self {
2433            Self::ReoptimizeImportedViews
2434            | Self::EnableNewOuterJoinLowering
2435            | Self::EnableEagerDeltaJoins
2436            | Self::EnableVariadicLeftJoinLowering
2437            | Self::EnableLetrecFixpointAnalysis
2438            | Self::EnableJoinPrioritizeArranged
2439            | Self::EnableProjectionPushdownAfterRelationCse => false,
2440        }
2441    }
2442}
2443
2444#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2445pub struct ClusterFeature<T: AstInfo> {
2446    pub name: ClusterFeatureName,
2447    pub value: Option<WithOptionValue<T>>,
2448}
2449impl_display_for_with_option!(ClusterFeature);
2450
2451/// `CREATE CLUSTER ..`
2452#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2453pub struct CreateClusterStatement<T: AstInfo> {
2454    /// Name of the created cluster.
2455    pub name: Ident,
2456    /// The comma-separated options.
2457    pub options: Vec<ClusterOption<T>>,
2458    /// The comma-separated features enabled on the cluster.
2459    pub features: Vec<ClusterFeature<T>>,
2460}
2461
2462impl<T: AstInfo> AstDisplay for CreateClusterStatement<T> {
2463    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2464        f.write_str("CREATE CLUSTER ");
2465        f.write_node(&self.name);
2466        if !self.options.is_empty() {
2467            f.write_str(" (");
2468            f.write_node(&display::comma_separated(&self.options));
2469            f.write_str(")");
2470        }
2471        if !self.features.is_empty() {
2472            f.write_str(" FEATURES (");
2473            f.write_node(&display::comma_separated(&self.features));
2474            f.write_str(")");
2475        }
2476    }
2477}
2478impl_display_t!(CreateClusterStatement);
2479
2480#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2481pub struct ReplicaDefinition<T: AstInfo> {
2482    /// Name of the created replica.
2483    pub name: Ident,
2484    /// The comma-separated options.
2485    pub options: Vec<ReplicaOption<T>>,
2486}
2487
2488// Note that this display is meant for replicas defined inline when creating
2489// clusters.
2490impl<T: AstInfo> AstDisplay for ReplicaDefinition<T> {
2491    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2492        f.write_node(&self.name);
2493        f.write_str(" (");
2494        f.write_node(&display::comma_separated(&self.options));
2495        f.write_str(")");
2496    }
2497}
2498impl_display_t!(ReplicaDefinition);
2499
2500#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2501pub enum AlterClusterAction<T: AstInfo> {
2502    SetOptions {
2503        options: Vec<ClusterOption<T>>,
2504        with_options: Vec<ClusterAlterOption<T>>,
2505    },
2506    ResetOptions(Vec<ClusterOptionName>),
2507}
2508
2509/// `ALTER CLUSTER .. SET ...`
2510#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2511pub struct AlterClusterStatement<T: AstInfo> {
2512    /// The `IF EXISTS` option.
2513    pub if_exists: bool,
2514    /// Name of the altered cluster.
2515    pub name: Ident,
2516    /// The action.
2517    pub action: AlterClusterAction<T>,
2518}
2519
2520impl<T: AstInfo> AstDisplay for AlterClusterStatement<T> {
2521    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2522        f.write_str("ALTER CLUSTER ");
2523        if self.if_exists {
2524            f.write_str("IF EXISTS ");
2525        }
2526        f.write_node(&self.name);
2527        f.write_str(" ");
2528        match &self.action {
2529            AlterClusterAction::SetOptions {
2530                options,
2531                with_options,
2532            } => {
2533                f.write_str("SET (");
2534                f.write_node(&display::comma_separated(options));
2535                f.write_str(")");
2536                if !with_options.is_empty() {
2537                    f.write_str(" WITH (");
2538                    f.write_node(&display::comma_separated(with_options));
2539                    f.write_str(")");
2540                }
2541            }
2542            AlterClusterAction::ResetOptions(options) => {
2543                f.write_str("RESET (");
2544                f.write_node(&display::comma_separated(options));
2545                f.write_str(")");
2546            }
2547        }
2548    }
2549}
2550impl_display_t!(AlterClusterStatement);
2551
2552/// `CREATE CLUSTER REPLICA ..`
2553#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2554pub struct CreateClusterReplicaStatement<T: AstInfo> {
2555    /// Name of the replica's cluster.
2556    pub of_cluster: Ident,
2557    /// The replica's definition.
2558    pub definition: ReplicaDefinition<T>,
2559}
2560
2561impl<T: AstInfo> AstDisplay for CreateClusterReplicaStatement<T> {
2562    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2563        f.write_str("CREATE CLUSTER REPLICA ");
2564        f.write_node(&self.of_cluster);
2565        f.write_str(".");
2566        f.write_node(&self.definition.name);
2567        f.write_str(" (");
2568        f.write_node(&display::comma_separated(&self.definition.options));
2569        f.write_str(")");
2570    }
2571}
2572impl_display_t!(CreateClusterReplicaStatement);
2573
2574#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2575pub enum ReplicaOptionName {
2576    /// The `BILLED AS [=] <value>` option.
2577    BilledAs,
2578    /// The `SIZE [[=] <size>]` option.
2579    Size,
2580    /// The `AVAILABILITY ZONE [[=] <id>]` option.
2581    AvailabilityZone,
2582    /// The `STORAGE ADDRESSES` option.
2583    StorageAddresses,
2584    /// The `STORAGECTL ADDRESSES` option.
2585    StoragectlAddresses,
2586    /// The `COMPUTECTL ADDRESSES` option.
2587    ComputectlAddresses,
2588    /// The `COMPUTE ADDRESSES` option.
2589    ComputeAddresses,
2590    /// The `WORKERS` option.
2591    Workers,
2592    /// The `INTERNAL` option.
2593    Internal,
2594    /// The `INTROSPECTION INTERVAL [[=] <interval>]` option.
2595    IntrospectionInterval,
2596    /// The `INTROSPECTION DEBUGGING [[=] <enabled>]` option.
2597    IntrospectionDebugging,
2598    /// The `DISK [[=] <enabled>]` option.
2599    Disk,
2600}
2601
2602impl AstDisplay for ReplicaOptionName {
2603    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2604        match self {
2605            ReplicaOptionName::BilledAs => f.write_str("BILLED AS"),
2606            ReplicaOptionName::Size => f.write_str("SIZE"),
2607            ReplicaOptionName::AvailabilityZone => f.write_str("AVAILABILITY ZONE"),
2608            ReplicaOptionName::StorageAddresses => f.write_str("STORAGE ADDRESSES"),
2609            ReplicaOptionName::StoragectlAddresses => f.write_str("STORAGECTL ADDRESSES"),
2610            ReplicaOptionName::ComputectlAddresses => f.write_str("COMPUTECTL ADDRESSES"),
2611            ReplicaOptionName::ComputeAddresses => f.write_str("COMPUTE ADDRESSES"),
2612            ReplicaOptionName::Workers => f.write_str("WORKERS"),
2613            ReplicaOptionName::Internal => f.write_str("INTERNAL"),
2614            ReplicaOptionName::IntrospectionInterval => f.write_str("INTROSPECTION INTERVAL"),
2615            ReplicaOptionName::IntrospectionDebugging => f.write_str("INTROSPECTION DEBUGGING"),
2616            ReplicaOptionName::Disk => f.write_str("DISK"),
2617        }
2618    }
2619}
2620
2621impl WithOptionName for ReplicaOptionName {
2622    /// # WARNING
2623    ///
2624    /// Whenever implementing this trait consider very carefully whether or not
2625    /// this value could contain sensitive user data. If you're uncertain, err
2626    /// on the conservative side and return `true`.
2627    fn redact_value(&self) -> bool {
2628        match self {
2629            ReplicaOptionName::BilledAs
2630            | ReplicaOptionName::Size
2631            | ReplicaOptionName::AvailabilityZone
2632            | ReplicaOptionName::StorageAddresses
2633            | ReplicaOptionName::StoragectlAddresses
2634            | ReplicaOptionName::ComputectlAddresses
2635            | ReplicaOptionName::ComputeAddresses
2636            | ReplicaOptionName::Workers
2637            | ReplicaOptionName::Internal
2638            | ReplicaOptionName::IntrospectionInterval
2639            | ReplicaOptionName::IntrospectionDebugging
2640            | ReplicaOptionName::Disk => false,
2641        }
2642    }
2643}
2644
2645#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2646/// An option in a `CREATE CLUSTER REPLICA` statement.
2647pub struct ReplicaOption<T: AstInfo> {
2648    pub name: ReplicaOptionName,
2649    pub value: Option<WithOptionValue<T>>,
2650}
2651impl_display_for_with_option!(ReplicaOption);
2652
2653/// `CREATE TYPE .. AS <TYPE>`
2654#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2655pub enum CreateTypeAs<T: AstInfo> {
2656    List {
2657        options: Vec<CreateTypeListOption<T>>,
2658    },
2659    Map {
2660        options: Vec<CreateTypeMapOption<T>>,
2661    },
2662    Record {
2663        column_defs: Vec<ColumnDef<T>>,
2664    },
2665}
2666
2667impl<T: AstInfo> AstDisplay for CreateTypeAs<T> {
2668    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2669        match self {
2670            CreateTypeAs::List { .. } => f.write_str("LIST "),
2671            CreateTypeAs::Map { .. } => f.write_str("MAP "),
2672            CreateTypeAs::Record { .. } => f.write_str("RECORD "),
2673        }
2674    }
2675}
2676impl_display_t!(CreateTypeAs);
2677
2678#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2679pub enum CreateTypeListOptionName {
2680    ElementType,
2681}
2682
2683impl AstDisplay for CreateTypeListOptionName {
2684    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2685        f.write_str(match self {
2686            CreateTypeListOptionName::ElementType => "ELEMENT TYPE",
2687        })
2688    }
2689}
2690
2691impl WithOptionName for CreateTypeListOptionName {
2692    /// # WARNING
2693    ///
2694    /// Whenever implementing this trait consider very carefully whether or not
2695    /// this value could contain sensitive user data. If you're uncertain, err
2696    /// on the conservative side and return `true`.
2697    fn redact_value(&self) -> bool {
2698        match self {
2699            CreateTypeListOptionName::ElementType => false,
2700        }
2701    }
2702}
2703
2704#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2705pub struct CreateTypeListOption<T: AstInfo> {
2706    pub name: CreateTypeListOptionName,
2707    pub value: Option<WithOptionValue<T>>,
2708}
2709impl_display_for_with_option!(CreateTypeListOption);
2710
2711#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2712pub enum CreateTypeMapOptionName {
2713    KeyType,
2714    ValueType,
2715}
2716
2717impl AstDisplay for CreateTypeMapOptionName {
2718    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2719        f.write_str(match self {
2720            CreateTypeMapOptionName::KeyType => "KEY TYPE",
2721            CreateTypeMapOptionName::ValueType => "VALUE TYPE",
2722        })
2723    }
2724}
2725
2726impl WithOptionName for CreateTypeMapOptionName {
2727    /// # WARNING
2728    ///
2729    /// Whenever implementing this trait consider very carefully whether or not
2730    /// this value could contain sensitive user data. If you're uncertain, err
2731    /// on the conservative side and return `true`.
2732    fn redact_value(&self) -> bool {
2733        match self {
2734            CreateTypeMapOptionName::KeyType | CreateTypeMapOptionName::ValueType => false,
2735        }
2736    }
2737}
2738
2739#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2740pub struct CreateTypeMapOption<T: AstInfo> {
2741    pub name: CreateTypeMapOptionName,
2742    pub value: Option<WithOptionValue<T>>,
2743}
2744impl_display_for_with_option!(CreateTypeMapOption);
2745
2746/// `ALTER <OBJECT> ... OWNER TO`
2747#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2748pub struct AlterOwnerStatement<T: AstInfo> {
2749    pub object_type: ObjectType,
2750    pub if_exists: bool,
2751    pub name: UnresolvedObjectName,
2752    pub new_owner: T::RoleName,
2753}
2754
2755impl<T: AstInfo> AstDisplay for AlterOwnerStatement<T> {
2756    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2757        f.write_str("ALTER ");
2758        f.write_node(&self.object_type);
2759        f.write_str(" ");
2760        if self.if_exists {
2761            f.write_str("IF EXISTS ");
2762        }
2763        f.write_node(&self.name);
2764        f.write_str(" OWNER TO ");
2765        f.write_node(&self.new_owner);
2766    }
2767}
2768impl_display_t!(AlterOwnerStatement);
2769
2770/// `ALTER <OBJECT> ... RENAME TO`
2771#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2772pub struct AlterObjectRenameStatement {
2773    pub object_type: ObjectType,
2774    pub if_exists: bool,
2775    pub name: UnresolvedObjectName,
2776    pub to_item_name: Ident,
2777}
2778
2779impl AstDisplay for AlterObjectRenameStatement {
2780    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2781        f.write_str("ALTER ");
2782        f.write_node(&self.object_type);
2783        f.write_str(" ");
2784        if self.if_exists {
2785            f.write_str("IF EXISTS ");
2786        }
2787        f.write_node(&self.name);
2788        f.write_str(" RENAME TO ");
2789        f.write_node(&self.to_item_name);
2790    }
2791}
2792impl_display!(AlterObjectRenameStatement);
2793
2794/// `ALTER <OBJECT> ... [RE]SET (RETAIN HISTORY [FOR ...])`
2795#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2796pub struct AlterRetainHistoryStatement<T: AstInfo> {
2797    pub object_type: ObjectType,
2798    pub if_exists: bool,
2799    pub name: UnresolvedObjectName,
2800    pub history: Option<WithOptionValue<T>>,
2801}
2802
2803impl<T: AstInfo> AstDisplay for AlterRetainHistoryStatement<T> {
2804    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2805        f.write_str("ALTER ");
2806        f.write_node(&self.object_type);
2807        f.write_str(" ");
2808        if self.if_exists {
2809            f.write_str("IF EXISTS ");
2810        }
2811        f.write_node(&self.name);
2812        if let Some(history) = &self.history {
2813            f.write_str(" SET (RETAIN HISTORY ");
2814            f.write_node(history);
2815        } else {
2816            f.write_str(" RESET (RETAIN HISTORY");
2817        }
2818        f.write_str(")");
2819    }
2820}
2821impl_display_t!(AlterRetainHistoryStatement);
2822
2823/// `ALTER <OBJECT> SWAP ...`
2824#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2825pub struct AlterObjectSwapStatement {
2826    pub object_type: ObjectType,
2827    pub if_exists: bool,
2828    pub name_a: UnresolvedObjectName,
2829    pub name_b: Ident,
2830}
2831
2832impl AstDisplay for AlterObjectSwapStatement {
2833    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2834        f.write_str("ALTER ");
2835
2836        f.write_node(&self.object_type);
2837        f.write_str(" ");
2838        if self.if_exists {
2839            f.write_str("IF EXISTS ");
2840        }
2841        f.write_node(&self.name_a);
2842
2843        f.write_str(" SWAP WITH ");
2844        f.write_node(&self.name_b);
2845    }
2846}
2847impl_display!(AlterObjectSwapStatement);
2848
2849#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2850pub enum AlterIndexAction<T: AstInfo> {
2851    SetOptions(Vec<IndexOption<T>>),
2852    ResetOptions(Vec<IndexOptionName>),
2853}
2854
2855/// `ALTER INDEX ... {RESET, SET}`
2856#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2857pub struct AlterIndexStatement<T: AstInfo> {
2858    pub index_name: UnresolvedItemName,
2859    pub if_exists: bool,
2860    pub action: AlterIndexAction<T>,
2861}
2862
2863impl<T: AstInfo> AstDisplay for AlterIndexStatement<T> {
2864    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2865        f.write_str("ALTER INDEX ");
2866        if self.if_exists {
2867            f.write_str("IF EXISTS ");
2868        }
2869        f.write_node(&self.index_name);
2870        f.write_str(" ");
2871
2872        match &self.action {
2873            AlterIndexAction::SetOptions(options) => {
2874                f.write_str("SET (");
2875                f.write_node(&display::comma_separated(options));
2876                f.write_str(")");
2877            }
2878            AlterIndexAction::ResetOptions(options) => {
2879                f.write_str("RESET (");
2880                f.write_node(&display::comma_separated(options));
2881                f.write_str(")");
2882            }
2883        }
2884    }
2885}
2886
2887impl_display_t!(AlterIndexStatement);
2888
2889#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2890pub enum AlterSinkAction<T: AstInfo> {
2891    SetOptions(Vec<CreateSinkOption<T>>),
2892    ResetOptions(Vec<CreateSinkOptionName>),
2893    ChangeRelation(T::ItemName),
2894}
2895
2896#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2897pub struct AlterSinkStatement<T: AstInfo> {
2898    pub sink_name: UnresolvedItemName,
2899    pub if_exists: bool,
2900    pub action: AlterSinkAction<T>,
2901}
2902
2903impl<T: AstInfo> AstDisplay for AlterSinkStatement<T> {
2904    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2905        f.write_str("ALTER SINK ");
2906        if self.if_exists {
2907            f.write_str("IF EXISTS ");
2908        }
2909        f.write_node(&self.sink_name);
2910        f.write_str(" ");
2911
2912        match &self.action {
2913            AlterSinkAction::ChangeRelation(from) => {
2914                f.write_str("SET FROM ");
2915                f.write_node(from);
2916            }
2917            AlterSinkAction::SetOptions(options) => {
2918                f.write_str("SET (");
2919                f.write_node(&display::comma_separated(options));
2920                f.write_str(")");
2921            }
2922            AlterSinkAction::ResetOptions(options) => {
2923                f.write_str("RESET (");
2924                f.write_node(&display::comma_separated(options));
2925                f.write_str(")");
2926            }
2927        }
2928    }
2929}
2930
2931#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2932pub enum AlterSourceAddSubsourceOptionName {
2933    /// Columns whose types you want to unconditionally format as text
2934    TextColumns,
2935    /// Columns you want to ignore when ingesting data
2936    ExcludeColumns,
2937    /// Updated `DETAILS` for an ingestion, e.g.
2938    /// [`crate::ast::PgConfigOptionName::Details`]
2939    /// or
2940    /// [`crate::ast::MySqlConfigOptionName::Details`].
2941    Details,
2942}
2943
2944impl AstDisplay for AlterSourceAddSubsourceOptionName {
2945    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
2946        f.write_str(match self {
2947            AlterSourceAddSubsourceOptionName::TextColumns => "TEXT COLUMNS",
2948            AlterSourceAddSubsourceOptionName::ExcludeColumns => "EXCLUDE COLUMNS",
2949            AlterSourceAddSubsourceOptionName::Details => "DETAILS",
2950        })
2951    }
2952}
2953impl_display!(AlterSourceAddSubsourceOptionName);
2954
2955impl WithOptionName for AlterSourceAddSubsourceOptionName {
2956    /// # WARNING
2957    ///
2958    /// Whenever implementing this trait consider very carefully whether or not
2959    /// this value could contain sensitive user data. If you're uncertain, err
2960    /// on the conservative side and return `true`.
2961    fn redact_value(&self) -> bool {
2962        match self {
2963            AlterSourceAddSubsourceOptionName::Details
2964            | AlterSourceAddSubsourceOptionName::TextColumns
2965            | AlterSourceAddSubsourceOptionName::ExcludeColumns => false,
2966        }
2967    }
2968}
2969
2970#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2971/// An option in an `ALTER SOURCE...ADD SUBSOURCE` statement.
2972pub struct AlterSourceAddSubsourceOption<T: AstInfo> {
2973    pub name: AlterSourceAddSubsourceOptionName,
2974    pub value: Option<WithOptionValue<T>>,
2975}
2976impl_display_for_with_option!(AlterSourceAddSubsourceOption);
2977impl_display_t!(AlterSourceAddSubsourceOption);
2978
2979#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2980pub enum AlterSourceAction<T: AstInfo> {
2981    SetOptions(Vec<CreateSourceOption<T>>),
2982    ResetOptions(Vec<CreateSourceOptionName>),
2983    AddSubsources {
2984        external_references: Vec<ExternalReferenceExport>,
2985        options: Vec<AlterSourceAddSubsourceOption<T>>,
2986    },
2987    DropSubsources {
2988        if_exists: bool,
2989        cascade: bool,
2990        names: Vec<UnresolvedItemName>,
2991    },
2992    RefreshReferences,
2993}
2994
2995impl<T: AstInfo> AstDisplay for AlterSourceAction<T> {
2996    fn fmt<W>(&self, f: &mut AstFormatter<W>)
2997    where
2998        W: fmt::Write,
2999    {
3000        match &self {
3001            AlterSourceAction::SetOptions(options) => {
3002                f.write_str("SET (");
3003                f.write_node(&display::comma_separated(options));
3004                f.write_str(")");
3005            }
3006            AlterSourceAction::ResetOptions(options) => {
3007                f.write_str("RESET (");
3008                f.write_node(&display::comma_separated(options));
3009                f.write_str(")");
3010            }
3011            AlterSourceAction::DropSubsources {
3012                if_exists,
3013                cascade,
3014                names,
3015            } => {
3016                f.write_str("DROP SUBSOURCE ");
3017                if *if_exists {
3018                    f.write_str("IF EXISTS ");
3019                }
3020
3021                f.write_node(&display::comma_separated(names));
3022
3023                if *cascade {
3024                    f.write_str(" CASCADE");
3025                }
3026            }
3027            AlterSourceAction::AddSubsources {
3028                external_references: subsources,
3029                options,
3030            } => {
3031                f.write_str("ADD SUBSOURCE ");
3032
3033                f.write_node(&display::comma_separated(subsources));
3034
3035                if !options.is_empty() {
3036                    f.write_str(" WITH (");
3037                    f.write_node(&display::comma_separated(options));
3038                    f.write_str(")");
3039                }
3040            }
3041            AlterSourceAction::RefreshReferences => {
3042                f.write_str("REFRESH REFERENCES");
3043            }
3044        }
3045    }
3046}
3047
3048#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3049pub struct AlterSourceStatement<T: AstInfo> {
3050    pub source_name: UnresolvedItemName,
3051    pub if_exists: bool,
3052    pub action: AlterSourceAction<T>,
3053}
3054
3055impl<T: AstInfo> AstDisplay for AlterSourceStatement<T> {
3056    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3057        f.write_str("ALTER SOURCE ");
3058        if self.if_exists {
3059            f.write_str("IF EXISTS ");
3060        }
3061        f.write_node(&self.source_name);
3062        f.write_str(" ");
3063        f.write_node(&self.action)
3064    }
3065}
3066
3067impl_display_t!(AlterSourceStatement);
3068
3069/// `ALTER SECRET ... AS`
3070#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3071pub struct AlterSecretStatement<T: AstInfo> {
3072    pub name: UnresolvedItemName,
3073    pub if_exists: bool,
3074    pub value: Expr<T>,
3075}
3076
3077impl<T: AstInfo> AstDisplay for AlterSecretStatement<T> {
3078    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3079        f.write_str("ALTER SECRET ");
3080        if self.if_exists {
3081            f.write_str("IF EXISTS ");
3082        }
3083        f.write_node(&self.name);
3084        f.write_str(" AS ");
3085
3086        if f.redacted() {
3087            f.write_str("'<REDACTED>'");
3088        } else {
3089            f.write_node(&self.value);
3090        }
3091    }
3092}
3093
3094impl_display_t!(AlterSecretStatement);
3095
3096#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3097pub enum AlterConnectionAction<T: AstInfo> {
3098    RotateKeys,
3099    SetOption(ConnectionOption<T>),
3100    DropOption(ConnectionOptionName),
3101}
3102
3103impl<T: AstInfo> AstDisplay for AlterConnectionAction<T> {
3104    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3105        match self {
3106            AlterConnectionAction::RotateKeys => f.write_str("ROTATE KEYS"),
3107            AlterConnectionAction::SetOption(option) => {
3108                f.write_str("SET (");
3109                f.write_node(option);
3110                f.write_str(")");
3111            }
3112            AlterConnectionAction::DropOption(option) => {
3113                f.write_str("DROP (");
3114                f.write_node(option);
3115                f.write_str(")");
3116            }
3117        }
3118    }
3119}
3120impl_display_t!(AlterConnectionAction);
3121
3122#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3123pub enum AlterConnectionOptionName {
3124    Validate,
3125}
3126
3127impl AstDisplay for AlterConnectionOptionName {
3128    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3129        f.write_str(match self {
3130            AlterConnectionOptionName::Validate => "VALIDATE",
3131        })
3132    }
3133}
3134impl_display!(AlterConnectionOptionName);
3135
3136impl WithOptionName for AlterConnectionOptionName {
3137    /// # WARNING
3138    ///
3139    /// Whenever implementing this trait consider very carefully whether or not
3140    /// this value could contain sensitive user data. If you're uncertain, err
3141    /// on the conservative side and return `true`.
3142    fn redact_value(&self) -> bool {
3143        match self {
3144            AlterConnectionOptionName::Validate => false,
3145        }
3146    }
3147}
3148
3149#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3150/// An option in an `ALTER CONNECTION...` statement.
3151pub struct AlterConnectionOption<T: AstInfo> {
3152    pub name: AlterConnectionOptionName,
3153    pub value: Option<WithOptionValue<T>>,
3154}
3155impl_display_for_with_option!(AlterConnectionOption);
3156impl_display_t!(AlterConnectionOption);
3157
3158/// `ALTER CONNECTION`
3159#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3160pub struct AlterConnectionStatement<T: AstInfo> {
3161    pub name: UnresolvedItemName,
3162    pub if_exists: bool,
3163    pub actions: Vec<AlterConnectionAction<T>>,
3164    pub with_options: Vec<AlterConnectionOption<T>>,
3165}
3166
3167impl<T: AstInfo> AstDisplay for AlterConnectionStatement<T> {
3168    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3169        f.write_str("ALTER CONNECTION ");
3170        if self.if_exists {
3171            f.write_str("IF EXISTS ");
3172        }
3173        f.write_node(&self.name);
3174        f.write_str(" ");
3175        f.write_node(&display::comma_separated(&self.actions));
3176
3177        if !self.with_options.is_empty() {
3178            f.write_str(" WITH (");
3179            f.write_node(&display::comma_separated(&self.with_options));
3180            f.write_str(")");
3181        }
3182    }
3183}
3184
3185impl_display_t!(AlterConnectionStatement);
3186
3187/// `ALTER ROLE`
3188#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3189pub struct AlterRoleStatement<T: AstInfo> {
3190    /// The specified role.
3191    pub name: T::RoleName,
3192    /// Alterations we're making to the role.
3193    pub option: AlterRoleOption,
3194}
3195
3196impl<T: AstInfo> AstDisplay for AlterRoleStatement<T> {
3197    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3198        f.write_str("ALTER ROLE ");
3199        f.write_node(&self.name);
3200        f.write_node(&self.option);
3201    }
3202}
3203impl_display_t!(AlterRoleStatement);
3204
3205/// `ALTER ROLE ... [ WITH | SET ] ...`
3206#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3207pub enum AlterRoleOption {
3208    /// Any options that were attached, in the order they were presented.
3209    Attributes(Vec<RoleAttribute>),
3210    /// A variable that we want to provide a default value for this role.
3211    Variable(SetRoleVar),
3212}
3213
3214impl AstDisplay for AlterRoleOption {
3215    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3216        match self {
3217            AlterRoleOption::Attributes(attrs) => {
3218                for attr in attrs {
3219                    f.write_str(" ");
3220                    attr.fmt(f)
3221                }
3222            }
3223            AlterRoleOption::Variable(var) => {
3224                f.write_str(" ");
3225                f.write_node(var);
3226            }
3227        }
3228    }
3229}
3230impl_display!(AlterRoleOption);
3231
3232/// `ALTER TABLE ... ADD COLUMN ...`
3233#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3234pub struct AlterTableAddColumnStatement<T: AstInfo> {
3235    pub if_exists: bool,
3236    pub name: UnresolvedItemName,
3237    pub if_col_not_exist: bool,
3238    pub column_name: Ident,
3239    pub data_type: T::DataType,
3240}
3241
3242impl<T: AstInfo> AstDisplay for AlterTableAddColumnStatement<T> {
3243    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3244    where
3245        W: fmt::Write,
3246    {
3247        f.write_str("ALTER TABLE ");
3248        if self.if_exists {
3249            f.write_str("IF EXISTS ");
3250        }
3251        f.write_node(&self.name);
3252
3253        f.write_str(" ADD COLUMN ");
3254        if self.if_col_not_exist {
3255            f.write_str("IF NOT EXISTS ");
3256        }
3257
3258        f.write_node(&self.column_name);
3259        f.write_str(" ");
3260        f.write_node(&self.data_type);
3261    }
3262}
3263
3264impl_display_t!(AlterTableAddColumnStatement);
3265
3266/// `ALTER MATERIALIZED VIEW ... APPLY REPLACEMENT ...`
3267#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3268pub struct AlterMaterializedViewApplyReplacementStatement {
3269    pub if_exists: bool,
3270    pub name: UnresolvedItemName,
3271    pub replacement_name: UnresolvedItemName,
3272}
3273
3274impl AstDisplay for AlterMaterializedViewApplyReplacementStatement {
3275    fn fmt<W>(&self, f: &mut AstFormatter<W>)
3276    where
3277        W: fmt::Write,
3278    {
3279        f.write_str("ALTER MATERIALIZED VIEW ");
3280        if self.if_exists {
3281            f.write_str("IF EXISTS ");
3282        }
3283        f.write_node(&self.name);
3284
3285        f.write_str(" APPLY REPLACEMENT ");
3286        f.write_node(&self.replacement_name);
3287    }
3288}
3289
3290impl_display!(AlterMaterializedViewApplyReplacementStatement);
3291
3292#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3293pub struct DiscardStatement {
3294    pub target: DiscardTarget,
3295}
3296
3297impl AstDisplay for DiscardStatement {
3298    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3299        f.write_str("DISCARD ");
3300        f.write_node(&self.target);
3301    }
3302}
3303impl_display!(DiscardStatement);
3304
3305#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3306pub enum DiscardTarget {
3307    Plans,
3308    Sequences,
3309    Temp,
3310    All,
3311}
3312
3313impl AstDisplay for DiscardTarget {
3314    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3315        match self {
3316            DiscardTarget::Plans => f.write_str("PLANS"),
3317            DiscardTarget::Sequences => f.write_str("SEQUENCES"),
3318            DiscardTarget::Temp => f.write_str("TEMP"),
3319            DiscardTarget::All => f.write_str("ALL"),
3320        }
3321    }
3322}
3323impl_display!(DiscardTarget);
3324
3325/// `DROP`
3326#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3327pub struct DropObjectsStatement {
3328    /// The type of the object to drop: TABLE, VIEW, etc.
3329    pub object_type: ObjectType,
3330    /// An optional `IF EXISTS` clause. (Non-standard.)
3331    pub if_exists: bool,
3332    /// One or more objects to drop. (ANSI SQL requires exactly one.)
3333    pub names: Vec<UnresolvedObjectName>,
3334    /// Whether `CASCADE` was specified. This will be `false` when
3335    /// `RESTRICT` was specified.
3336    pub cascade: bool,
3337}
3338
3339impl AstDisplay for DropObjectsStatement {
3340    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3341        f.write_str("DROP ");
3342        f.write_node(&self.object_type);
3343        f.write_str(" ");
3344        if self.if_exists {
3345            f.write_str("IF EXISTS ");
3346        }
3347        f.write_node(&display::comma_separated(&self.names));
3348        if self.cascade && self.object_type != ObjectType::Database {
3349            f.write_str(" CASCADE");
3350        } else if !self.cascade && self.object_type == ObjectType::Database {
3351            f.write_str(" RESTRICT");
3352        }
3353    }
3354}
3355impl_display!(DropObjectsStatement);
3356
3357/// `DROP OWNED BY ...`
3358#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3359pub struct DropOwnedStatement<T: AstInfo> {
3360    /// The roles whose owned objects are being dropped.
3361    pub role_names: Vec<T::RoleName>,
3362    /// Whether `CASCADE` was specified. `false` for `RESTRICT` and `None` if no drop behavior at
3363    /// all was specified.
3364    pub cascade: Option<bool>,
3365}
3366
3367impl<T: AstInfo> DropOwnedStatement<T> {
3368    pub fn cascade(&self) -> bool {
3369        self.cascade == Some(true)
3370    }
3371}
3372
3373impl<T: AstInfo> AstDisplay for DropOwnedStatement<T> {
3374    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3375        f.write_str("DROP OWNED BY ");
3376        f.write_node(&display::comma_separated(&self.role_names));
3377        if let Some(true) = self.cascade {
3378            f.write_str(" CASCADE");
3379        } else if let Some(false) = self.cascade {
3380            f.write_str(" RESTRICT");
3381        }
3382    }
3383}
3384impl_display_t!(DropOwnedStatement);
3385
3386#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3387pub struct QualifiedReplica {
3388    pub cluster: Ident,
3389    pub replica: Ident,
3390}
3391
3392impl AstDisplay for QualifiedReplica {
3393    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3394        f.write_node(&self.cluster);
3395        f.write_str(".");
3396        f.write_node(&self.replica);
3397    }
3398}
3399impl_display!(QualifiedReplica);
3400
3401/// `SET <variable>`
3402///
3403/// Note: this is not a standard SQL statement, but it is supported by at
3404/// least MySQL and PostgreSQL. Not all MySQL-specific syntactic forms are
3405/// supported yet.
3406#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3407pub struct SetVariableStatement {
3408    pub local: bool,
3409    pub variable: Ident,
3410    pub to: SetVariableTo,
3411}
3412
3413impl AstDisplay for SetVariableStatement {
3414    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3415        f.write_str("SET ");
3416        if self.local {
3417            f.write_str("LOCAL ");
3418        }
3419        f.write_node(&self.variable);
3420        f.write_str(" = ");
3421        f.write_node(&self.to);
3422    }
3423}
3424impl_display!(SetVariableStatement);
3425
3426/// `RESET <variable>`
3427///
3428/// Note: this is not a standard SQL statement, but it is supported by at
3429/// least MySQL and PostgreSQL. Not all syntactic forms are supported yet.
3430#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3431pub struct ResetVariableStatement {
3432    pub variable: Ident,
3433}
3434
3435impl AstDisplay for ResetVariableStatement {
3436    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3437        f.write_str("RESET ");
3438        f.write_node(&self.variable);
3439    }
3440}
3441impl_display!(ResetVariableStatement);
3442
3443/// `SHOW <variable>`
3444#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3445pub struct ShowVariableStatement {
3446    pub variable: Ident,
3447}
3448
3449impl AstDisplay for ShowVariableStatement {
3450    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3451        f.write_str("SHOW ");
3452        f.write_node(&self.variable);
3453    }
3454}
3455impl_display!(ShowVariableStatement);
3456
3457/// `INSPECT SHARD <id>`
3458#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3459pub struct InspectShardStatement {
3460    pub id: String,
3461}
3462
3463impl AstDisplay for InspectShardStatement {
3464    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3465        f.write_str("INSPECT SHARD ");
3466        f.write_str("'");
3467        f.write_node(&display::escape_single_quote_string(&self.id));
3468        f.write_str("'");
3469    }
3470}
3471impl_display!(InspectShardStatement);
3472
3473#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3474pub enum ShowObjectType<T: AstInfo> {
3475    MaterializedView {
3476        in_cluster: Option<T::ClusterName>,
3477    },
3478    Index {
3479        in_cluster: Option<T::ClusterName>,
3480        on_object: Option<T::ItemName>,
3481    },
3482    Table {
3483        on_source: Option<T::ItemName>,
3484    },
3485    View,
3486    Source {
3487        in_cluster: Option<T::ClusterName>,
3488    },
3489    Sink {
3490        in_cluster: Option<T::ClusterName>,
3491    },
3492    Type,
3493    Role,
3494    Cluster,
3495    ClusterReplica,
3496    Object,
3497    Secret,
3498    Connection,
3499    Database,
3500    Schema {
3501        from: Option<T::DatabaseName>,
3502    },
3503    Subsource {
3504        on_source: Option<T::ItemName>,
3505    },
3506    Privileges {
3507        object_type: Option<SystemObjectType>,
3508        role: Option<T::RoleName>,
3509    },
3510    DefaultPrivileges {
3511        object_type: Option<ObjectType>,
3512        role: Option<T::RoleName>,
3513    },
3514    RoleMembership {
3515        role: Option<T::RoleName>,
3516    },
3517    NetworkPolicy,
3518}
3519/// `SHOW <object>S`
3520///
3521/// ```sql
3522/// SHOW TABLES;
3523/// SHOW SOURCES;
3524/// SHOW VIEWS;
3525/// SHOW SINKS;
3526/// ```
3527#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3528pub struct ShowObjectsStatement<T: AstInfo> {
3529    pub object_type: ShowObjectType<T>,
3530    pub from: Option<T::SchemaName>,
3531    pub filter: Option<ShowStatementFilter<T>>,
3532}
3533
3534impl<T: AstInfo> AstDisplay for ShowObjectsStatement<T> {
3535    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3536        f.write_str("SHOW");
3537        f.write_str(" ");
3538
3539        f.write_str(match &self.object_type {
3540            ShowObjectType::Table { .. } => "TABLES",
3541            ShowObjectType::View => "VIEWS",
3542            ShowObjectType::Source { .. } => "SOURCES",
3543            ShowObjectType::Sink { .. } => "SINKS",
3544            ShowObjectType::Type => "TYPES",
3545            ShowObjectType::Role => "ROLES",
3546            ShowObjectType::Cluster => "CLUSTERS",
3547            ShowObjectType::ClusterReplica => "CLUSTER REPLICAS",
3548            ShowObjectType::Object => "OBJECTS",
3549            ShowObjectType::Secret => "SECRETS",
3550            ShowObjectType::Connection => "CONNECTIONS",
3551            ShowObjectType::MaterializedView { .. } => "MATERIALIZED VIEWS",
3552            ShowObjectType::Index { .. } => "INDEXES",
3553            ShowObjectType::Database => "DATABASES",
3554            ShowObjectType::Schema { .. } => "SCHEMAS",
3555            ShowObjectType::Subsource { .. } => "SUBSOURCES",
3556            ShowObjectType::Privileges { .. } => "PRIVILEGES",
3557            ShowObjectType::DefaultPrivileges { .. } => "DEFAULT PRIVILEGES",
3558            ShowObjectType::RoleMembership { .. } => "ROLE MEMBERSHIP",
3559            ShowObjectType::NetworkPolicy => "NETWORK POLICIES",
3560        });
3561
3562        if let ShowObjectType::Index { on_object, .. } = &self.object_type {
3563            if let Some(on_object) = on_object {
3564                f.write_str(" ON ");
3565                f.write_node(on_object);
3566            }
3567        }
3568
3569        if let ShowObjectType::Schema { from: Some(from) } = &self.object_type {
3570            f.write_str(" FROM ");
3571            f.write_node(from);
3572        }
3573
3574        if let Some(from) = &self.from {
3575            f.write_str(" FROM ");
3576            f.write_node(from);
3577        }
3578
3579        // append IN CLUSTER clause
3580        match &self.object_type {
3581            ShowObjectType::MaterializedView { in_cluster }
3582            | ShowObjectType::Index { in_cluster, .. }
3583            | ShowObjectType::Sink { in_cluster }
3584            | ShowObjectType::Source { in_cluster } => {
3585                if let Some(cluster) = in_cluster {
3586                    f.write_str(" IN CLUSTER ");
3587                    f.write_node(cluster);
3588                }
3589            }
3590            _ => (),
3591        }
3592
3593        if let ShowObjectType::Subsource { on_source } = &self.object_type {
3594            if let Some(on_source) = on_source {
3595                f.write_str(" ON ");
3596                f.write_node(on_source);
3597            }
3598        }
3599
3600        if let ShowObjectType::Table { on_source } = &self.object_type {
3601            if let Some(on_source) = on_source {
3602                f.write_str(" ON ");
3603                f.write_node(on_source);
3604            }
3605        }
3606
3607        if let ShowObjectType::Privileges { object_type, role } = &self.object_type {
3608            if let Some(object_type) = object_type {
3609                f.write_str(" ON ");
3610                f.write_node(object_type);
3611                if let SystemObjectType::Object(_) = object_type {
3612                    f.write_str("S");
3613                }
3614            }
3615            if let Some(role) = role {
3616                f.write_str(" FOR ");
3617                f.write_node(role);
3618            }
3619        }
3620
3621        if let ShowObjectType::DefaultPrivileges { object_type, role } = &self.object_type {
3622            if let Some(object_type) = object_type {
3623                f.write_str(" ON ");
3624                f.write_node(object_type);
3625                f.write_str("S");
3626            }
3627            if let Some(role) = role {
3628                f.write_str(" FOR ");
3629                f.write_node(role);
3630            }
3631        }
3632
3633        if let ShowObjectType::RoleMembership {
3634            role: Some(role), ..
3635        } = &self.object_type
3636        {
3637            f.write_str(" FOR ");
3638            f.write_node(role);
3639        }
3640
3641        if let Some(filter) = &self.filter {
3642            f.write_str(" ");
3643            f.write_node(filter);
3644        }
3645    }
3646}
3647impl_display_t!(ShowObjectsStatement);
3648
3649/// `SHOW COLUMNS`
3650///
3651/// Note: this is a MySQL-specific statement.
3652#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3653pub struct ShowColumnsStatement<T: AstInfo> {
3654    pub table_name: T::ItemName,
3655    pub filter: Option<ShowStatementFilter<T>>,
3656}
3657
3658impl<T: AstInfo> AstDisplay for ShowColumnsStatement<T> {
3659    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3660        f.write_str("SHOW ");
3661        f.write_str("COLUMNS FROM ");
3662        f.write_node(&self.table_name);
3663        if let Some(filter) = &self.filter {
3664            f.write_str(" ");
3665            f.write_node(filter);
3666        }
3667    }
3668}
3669impl_display_t!(ShowColumnsStatement);
3670
3671/// `SHOW [REDACTED] CREATE VIEW <view>`
3672#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3673pub struct ShowCreateViewStatement<T: AstInfo> {
3674    pub view_name: T::ItemName,
3675    pub redacted: bool,
3676}
3677
3678impl<T: AstInfo> AstDisplay for ShowCreateViewStatement<T> {
3679    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3680        f.write_str("SHOW ");
3681        if self.redacted {
3682            f.write_str("REDACTED ");
3683        }
3684        f.write_str("CREATE VIEW ");
3685        f.write_node(&self.view_name);
3686    }
3687}
3688impl_display_t!(ShowCreateViewStatement);
3689
3690/// `SHOW [REDACTED] CREATE MATERIALIZED VIEW <name>`
3691#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3692pub struct ShowCreateMaterializedViewStatement<T: AstInfo> {
3693    pub materialized_view_name: T::ItemName,
3694    pub redacted: bool,
3695}
3696
3697impl<T: AstInfo> AstDisplay for ShowCreateMaterializedViewStatement<T> {
3698    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3699        f.write_str("SHOW ");
3700        if self.redacted {
3701            f.write_str("REDACTED ");
3702        }
3703        f.write_str("CREATE MATERIALIZED VIEW ");
3704        f.write_node(&self.materialized_view_name);
3705    }
3706}
3707impl_display_t!(ShowCreateMaterializedViewStatement);
3708
3709/// `SHOW [REDACTED] CREATE SOURCE <source>`
3710#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3711pub struct ShowCreateSourceStatement<T: AstInfo> {
3712    pub source_name: T::ItemName,
3713    pub redacted: bool,
3714}
3715
3716impl<T: AstInfo> AstDisplay for ShowCreateSourceStatement<T> {
3717    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3718        f.write_str("SHOW ");
3719        if self.redacted {
3720            f.write_str("REDACTED ");
3721        }
3722        f.write_str("CREATE SOURCE ");
3723        f.write_node(&self.source_name);
3724    }
3725}
3726impl_display_t!(ShowCreateSourceStatement);
3727
3728/// `SHOW [REDACTED] CREATE TABLE <table>`
3729#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3730pub struct ShowCreateTableStatement<T: AstInfo> {
3731    pub table_name: T::ItemName,
3732    pub redacted: bool,
3733}
3734
3735impl<T: AstInfo> AstDisplay for ShowCreateTableStatement<T> {
3736    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3737        f.write_str("SHOW ");
3738        if self.redacted {
3739            f.write_str("REDACTED ");
3740        }
3741        f.write_str("CREATE TABLE ");
3742        f.write_node(&self.table_name);
3743    }
3744}
3745impl_display_t!(ShowCreateTableStatement);
3746
3747/// `SHOW [REDACTED] CREATE SINK <sink>`
3748#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3749pub struct ShowCreateSinkStatement<T: AstInfo> {
3750    pub sink_name: T::ItemName,
3751    pub redacted: bool,
3752}
3753
3754impl<T: AstInfo> AstDisplay for ShowCreateSinkStatement<T> {
3755    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3756        f.write_str("SHOW ");
3757        if self.redacted {
3758            f.write_str("REDACTED ");
3759        }
3760        f.write_str("CREATE SINK ");
3761        f.write_node(&self.sink_name);
3762    }
3763}
3764impl_display_t!(ShowCreateSinkStatement);
3765
3766/// `SHOW [REDACTED] CREATE INDEX <index>`
3767#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3768pub struct ShowCreateIndexStatement<T: AstInfo> {
3769    pub index_name: T::ItemName,
3770    pub redacted: bool,
3771}
3772
3773impl<T: AstInfo> AstDisplay for ShowCreateIndexStatement<T> {
3774    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3775        f.write_str("SHOW ");
3776        if self.redacted {
3777            f.write_str("REDACTED ");
3778        }
3779        f.write_str("CREATE INDEX ");
3780        f.write_node(&self.index_name);
3781    }
3782}
3783impl_display_t!(ShowCreateIndexStatement);
3784
3785/// `SHOW [REDACTED] CREATE CONNECTION <connection>`
3786#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3787pub struct ShowCreateConnectionStatement<T: AstInfo> {
3788    pub connection_name: T::ItemName,
3789    pub redacted: bool,
3790}
3791
3792impl<T: AstInfo> AstDisplay for ShowCreateConnectionStatement<T> {
3793    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3794        f.write_str("SHOW ");
3795        if self.redacted {
3796            f.write_str("REDACTED ");
3797        }
3798        f.write_str("CREATE CONNECTION ");
3799        f.write_node(&self.connection_name);
3800    }
3801}
3802
3803#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3804pub struct ShowCreateClusterStatement<T: AstInfo> {
3805    pub cluster_name: T::ClusterName,
3806}
3807
3808impl<T: AstInfo> AstDisplay for ShowCreateClusterStatement<T> {
3809    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3810        f.write_str("SHOW CREATE CLUSTER ");
3811        f.write_node(&self.cluster_name);
3812    }
3813}
3814
3815/// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...`
3816#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3817pub struct StartTransactionStatement {
3818    pub modes: Vec<TransactionMode>,
3819}
3820
3821impl AstDisplay for StartTransactionStatement {
3822    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3823        f.write_str("START TRANSACTION");
3824        if !self.modes.is_empty() {
3825            f.write_str(" ");
3826            f.write_node(&display::comma_separated(&self.modes));
3827        }
3828    }
3829}
3830impl_display!(StartTransactionStatement);
3831
3832/// `SHOW [REDACTED] CREATE TYPE <type>`
3833#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3834pub struct ShowCreateTypeStatement<T: AstInfo> {
3835    pub type_name: T::DataType,
3836    pub redacted: bool,
3837}
3838
3839impl<T: AstInfo> AstDisplay for ShowCreateTypeStatement<T> {
3840    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3841        f.write_str("SHOW ");
3842        if self.redacted {
3843            f.write_str("REDACTED ");
3844        }
3845        f.write_str("CREATE TYPE ");
3846        f.write_node(&self.type_name);
3847    }
3848}
3849
3850/// `SET TRANSACTION ...`
3851#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
3852pub struct SetTransactionStatement {
3853    pub local: bool,
3854    pub modes: Vec<TransactionMode>,
3855}
3856
3857impl AstDisplay for SetTransactionStatement {
3858    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3859        f.write_str("SET ");
3860        if !self.local {
3861            f.write_str("SESSION CHARACTERISTICS AS ");
3862        }
3863        f.write_str("TRANSACTION");
3864        if !self.modes.is_empty() {
3865            f.write_str(" ");
3866            f.write_node(&display::comma_separated(&self.modes));
3867        }
3868    }
3869}
3870impl_display!(SetTransactionStatement);
3871
3872/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
3873#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3874pub struct CommitStatement {
3875    pub chain: bool,
3876}
3877
3878impl AstDisplay for CommitStatement {
3879    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3880        f.write_str("COMMIT");
3881        if self.chain {
3882            f.write_str(" AND CHAIN");
3883        }
3884    }
3885}
3886impl_display!(CommitStatement);
3887
3888/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
3889#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3890pub struct RollbackStatement {
3891    pub chain: bool,
3892}
3893
3894impl AstDisplay for RollbackStatement {
3895    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3896        f.write_str("ROLLBACK");
3897        if self.chain {
3898            f.write_str(" AND CHAIN");
3899        }
3900    }
3901}
3902impl_display!(RollbackStatement);
3903
3904#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3905pub enum SubscribeOptionName {
3906    Snapshot,
3907    Progress,
3908}
3909
3910impl AstDisplay for SubscribeOptionName {
3911    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3912        match self {
3913            SubscribeOptionName::Snapshot => f.write_str("SNAPSHOT"),
3914            SubscribeOptionName::Progress => f.write_str("PROGRESS"),
3915        }
3916    }
3917}
3918impl_display!(SubscribeOptionName);
3919
3920impl WithOptionName for SubscribeOptionName {
3921    /// # WARNING
3922    ///
3923    /// Whenever implementing this trait consider very carefully whether or not
3924    /// this value could contain sensitive user data. If you're uncertain, err
3925    /// on the conservative side and return `true`.
3926    fn redact_value(&self) -> bool {
3927        match self {
3928            SubscribeOptionName::Snapshot | SubscribeOptionName::Progress => false,
3929        }
3930    }
3931}
3932
3933#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3934pub struct SubscribeOption<T: AstInfo> {
3935    pub name: SubscribeOptionName,
3936    pub value: Option<WithOptionValue<T>>,
3937}
3938impl_display_for_with_option!(SubscribeOption);
3939impl_display_t!(SubscribeOption);
3940
3941/// `SUBSCRIBE`
3942#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3943pub struct SubscribeStatement<T: AstInfo> {
3944    pub relation: SubscribeRelation<T>,
3945    pub options: Vec<SubscribeOption<T>>,
3946    pub as_of: Option<AsOf<T>>,
3947    pub up_to: Option<Expr<T>>,
3948    pub output: SubscribeOutput<T>,
3949}
3950
3951impl<T: AstInfo> AstDisplay for SubscribeStatement<T> {
3952    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
3953        f.write_str("SUBSCRIBE ");
3954        if self.relation.needs_explicit_to(f.simple()) {
3955            // Without the optional `TO` keyword, a relation whose first name
3956            // component is the bare keyword `to` (e.g. `SUBSCRIBE TO to`) would
3957            // display as `SUBSCRIBE to`, which re-parses with `to` consumed as
3958            // the optional keyword, dropping the relation name.
3959            f.write_str("TO ");
3960        }
3961        f.write_node(&self.relation);
3962        if !self.options.is_empty() {
3963            f.write_str(" WITH (");
3964            f.write_node(&display::comma_separated(&self.options));
3965            f.write_str(")");
3966        }
3967        if let Some(as_of) = &self.as_of {
3968            f.write_str(" ");
3969            f.write_node(as_of);
3970        }
3971        if let Some(up_to) = &self.up_to {
3972            f.write_str(" UP TO ");
3973            f.write_node(up_to);
3974        }
3975        f.write_str(&self.output);
3976    }
3977}
3978impl_display_t!(SubscribeStatement);
3979
3980#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3981pub enum SubscribeRelation<T: AstInfo> {
3982    Name(T::ItemName),
3983    Query(Query<T>),
3984}
3985
3986impl<T: AstInfo> SubscribeRelation<T> {
3987    /// Reports whether printing this relation after `SUBSCRIBE` requires the
3988    /// optional `TO` keyword to avoid reparsing the first name component as that
3989    /// keyword instead of as part of the relation name.
3990    pub fn needs_explicit_to(&self, bare_identifiers: bool) -> bool {
3991        let SubscribeRelation::Name(name) = self else {
3992            return false;
3993        };
3994        bare_identifiers && name_starts_with_bare_to(&name.to_ast_string_simple())
3995    }
3996}
3997
3998fn name_starts_with_bare_to(name: &str) -> bool {
3999    let Some(rest) = name.strip_prefix("to") else {
4000        return false;
4001    };
4002    rest.is_empty() || rest.starts_with('.')
4003}
4004
4005impl<T: AstInfo> AstDisplay for SubscribeRelation<T> {
4006    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4007        match self {
4008            SubscribeRelation::Name(name) => f.write_node(name),
4009            SubscribeRelation::Query(query) => {
4010                f.write_str("(");
4011                f.write_node(query);
4012                f.write_str(")");
4013            }
4014        }
4015    }
4016}
4017impl_display_t!(SubscribeRelation);
4018
4019#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4020pub struct ExplainPlanStatement<T: AstInfo> {
4021    pub stage: Option<ExplainStage>,
4022    pub with_options: Vec<ExplainPlanOption<T>>,
4023    pub format: Option<ExplainFormat>,
4024    pub explainee: Explainee<T>,
4025}
4026
4027impl<T: AstInfo> ExplainPlanStatement<T> {
4028    pub fn stage(&self) -> ExplainStage {
4029        self.stage.unwrap_or(ExplainStage::PhysicalPlan)
4030    }
4031
4032    pub fn format(&self) -> ExplainFormat {
4033        self.format.unwrap_or(ExplainFormat::Text)
4034    }
4035}
4036
4037impl<T: AstInfo> AstDisplay for ExplainPlanStatement<T> {
4038    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4039        f.write_str("EXPLAIN");
4040        if let Some(stage) = &self.stage {
4041            f.write_str(" ");
4042            f.write_node(stage);
4043        }
4044        if !self.with_options.is_empty() {
4045            f.write_str(" WITH (");
4046            f.write_node(&display::comma_separated(&self.with_options));
4047            f.write_str(")");
4048        }
4049        if let Some(format) = &self.format {
4050            f.write_str(" AS ");
4051            f.write_node(format);
4052        }
4053        if self.stage.is_some() {
4054            f.write_str(" FOR");
4055        }
4056        f.write_str(" ");
4057        f.write_node(&self.explainee);
4058    }
4059}
4060impl_display_t!(ExplainPlanStatement);
4061
4062// Note: the `AstDisplay` implementation and `Parser::parse_` method for this
4063// enum are generated automatically by this crate's `build.rs`.
4064#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4065pub enum ExplainPlanOptionName {
4066    Arity,
4067    Cardinality,
4068    ColumnNames,
4069    FilterPushdown,
4070    HumanizedExpressions,
4071    JoinImplementations,
4072    Keys,
4073    LinearChains,
4074    NonNegative,
4075    NoFastPath,
4076    NoNotices,
4077    NodeIdentifiers,
4078    RawPlans,
4079    RawSyntax,
4080    Raw, // Listed after the `Raw~` variants to keep the parser happy!
4081    Redacted,
4082    SubtreeSize,
4083    Timing,
4084    Types,
4085    Equivalences,
4086    ReoptimizeImportedViews,
4087    EnableNewOuterJoinLowering,
4088    EnableEagerDeltaJoins,
4089    EnableVariadicLeftJoinLowering,
4090    EnableLetrecFixpointAnalysis,
4091    EnableJoinPrioritizeArranged,
4092    EnableProjectionPushdownAfterRelationCse,
4093    EnableFixedCorrelatedCteLowering,
4094}
4095
4096impl WithOptionName for ExplainPlanOptionName {
4097    /// # WARNING
4098    ///
4099    /// Whenever implementing this trait consider very carefully whether or not
4100    /// this value could contain sensitive user data. If you're uncertain, err
4101    /// on the conservative side and return `true`.
4102    fn redact_value(&self) -> bool {
4103        match self {
4104            Self::Arity
4105            | Self::Cardinality
4106            | Self::ColumnNames
4107            | Self::FilterPushdown
4108            | Self::HumanizedExpressions
4109            | Self::JoinImplementations
4110            | Self::Keys
4111            | Self::LinearChains
4112            | Self::NonNegative
4113            | Self::NoFastPath
4114            | Self::NoNotices
4115            | Self::NodeIdentifiers
4116            | Self::RawPlans
4117            | Self::RawSyntax
4118            | Self::Raw
4119            | Self::Redacted
4120            | Self::SubtreeSize
4121            | Self::Timing
4122            | Self::Types
4123            | Self::Equivalences
4124            | Self::ReoptimizeImportedViews
4125            | Self::EnableNewOuterJoinLowering
4126            | Self::EnableEagerDeltaJoins
4127            | Self::EnableVariadicLeftJoinLowering
4128            | Self::EnableLetrecFixpointAnalysis
4129            | Self::EnableJoinPrioritizeArranged
4130            | Self::EnableProjectionPushdownAfterRelationCse
4131            | Self::EnableFixedCorrelatedCteLowering => false,
4132        }
4133    }
4134}
4135
4136#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4137pub struct ExplainPlanOption<T: AstInfo> {
4138    pub name: ExplainPlanOptionName,
4139    pub value: Option<WithOptionValue<T>>,
4140}
4141impl_display_for_with_option!(ExplainPlanOption);
4142
4143#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4144pub enum ExplainSinkSchemaFor {
4145    Key,
4146    Value,
4147}
4148#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4149pub struct ExplainSinkSchemaStatement<T: AstInfo> {
4150    pub schema_for: ExplainSinkSchemaFor,
4151    pub format: Option<ExplainFormat>,
4152    pub statement: CreateSinkStatement<T>,
4153}
4154
4155impl<T: AstInfo> AstDisplay for ExplainSinkSchemaStatement<T> {
4156    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4157        f.write_str("EXPLAIN ");
4158        match &self.schema_for {
4159            ExplainSinkSchemaFor::Key => f.write_str("KEY"),
4160            ExplainSinkSchemaFor::Value => f.write_str("VALUE"),
4161        }
4162        f.write_str(" SCHEMA");
4163        if let Some(format) = &self.format {
4164            f.write_str(" AS ");
4165            f.write_node(format);
4166        }
4167        f.write_str(" FOR ");
4168        f.write_node(&self.statement);
4169    }
4170}
4171impl_display_t!(ExplainSinkSchemaStatement);
4172
4173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4174pub struct ExplainPushdownStatement<T: AstInfo> {
4175    pub explainee: Explainee<T>,
4176}
4177
4178impl<T: AstInfo> AstDisplay for ExplainPushdownStatement<T> {
4179    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4180        f.write_str("EXPLAIN FILTER PUSHDOWN FOR ");
4181        f.write_node(&self.explainee);
4182    }
4183}
4184impl_display_t!(ExplainPushdownStatement);
4185
4186#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4187pub enum ExplainAnalyzeComputationProperty {
4188    Cpu,
4189    Memory,
4190}
4191
4192#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4193pub enum ExplainAnalyzeProperty {
4194    Computation(ExplainAnalyzeComputationProperties),
4195    Hints,
4196}
4197
4198#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4199pub struct ExplainAnalyzeComputationProperties {
4200    /// Must be non-empty.
4201    pub properties: Vec<ExplainAnalyzeComputationProperty>,
4202    pub skew: bool,
4203}
4204#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4205pub struct ExplainAnalyzeObjectStatement<T: AstInfo> {
4206    pub properties: ExplainAnalyzeProperty,
4207    /// Should only be `Explainee::Index` or `Explainee::MaterializedView`
4208    pub explainee: Explainee<T>,
4209    pub as_sql: bool,
4210}
4211
4212impl<T: AstInfo> AstDisplay for ExplainAnalyzeObjectStatement<T> {
4213    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4214        f.write_str("EXPLAIN ANALYZE");
4215        match &self.properties {
4216            ExplainAnalyzeProperty::Computation(ExplainAnalyzeComputationProperties {
4217                properties,
4218                skew,
4219            }) => {
4220                let mut first = true;
4221                for property in properties {
4222                    if first {
4223                        first = false;
4224                    } else {
4225                        f.write_str(",");
4226                    }
4227                    match property {
4228                        ExplainAnalyzeComputationProperty::Cpu => f.write_str(" CPU"),
4229                        ExplainAnalyzeComputationProperty::Memory => f.write_str(" MEMORY"),
4230                    }
4231                }
4232                if *skew {
4233                    f.write_str(" WITH SKEW");
4234                }
4235            }
4236            ExplainAnalyzeProperty::Hints => f.write_str(" HINTS"),
4237        }
4238        f.write_str(" FOR ");
4239        f.write_node(&self.explainee);
4240        if self.as_sql {
4241            f.write_str(" AS SQL");
4242        }
4243    }
4244}
4245impl_display_t!(ExplainAnalyzeObjectStatement);
4246
4247#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4248pub struct ExplainAnalyzeClusterStatement {
4249    pub properties: ExplainAnalyzeComputationProperties,
4250    pub as_sql: bool,
4251}
4252
4253impl AstDisplay for ExplainAnalyzeClusterStatement {
4254    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4255        f.write_str("EXPLAIN ANALYZE CLUSTER");
4256
4257        let mut first = true;
4258        for property in &self.properties.properties {
4259            if first {
4260                first = false;
4261            } else {
4262                f.write_str(",");
4263            }
4264            match property {
4265                ExplainAnalyzeComputationProperty::Cpu => f.write_str(" CPU"),
4266                ExplainAnalyzeComputationProperty::Memory => f.write_str(" MEMORY"),
4267            }
4268        }
4269
4270        if self.properties.skew {
4271            f.write_str(" WITH SKEW");
4272        }
4273        if self.as_sql {
4274            f.write_str(" AS SQL");
4275        }
4276    }
4277}
4278impl_display!(ExplainAnalyzeClusterStatement);
4279
4280#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4281pub struct ExplainTimestampStatement<T: AstInfo> {
4282    pub format: Option<ExplainFormat>,
4283    pub select: SelectStatement<T>,
4284}
4285
4286impl<T: AstInfo> ExplainTimestampStatement<T> {
4287    pub fn format(&self) -> ExplainFormat {
4288        self.format.unwrap_or(ExplainFormat::Text)
4289    }
4290}
4291
4292impl<T: AstInfo> AstDisplay for ExplainTimestampStatement<T> {
4293    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4294        f.write_str("EXPLAIN TIMESTAMP");
4295        if let Some(format) = &self.format {
4296            f.write_str(" AS ");
4297            f.write_node(format);
4298        }
4299        f.write_str(" FOR ");
4300        f.write_node(&self.select);
4301    }
4302}
4303impl_display_t!(ExplainTimestampStatement);
4304
4305#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4306pub enum InsertSource<T: AstInfo> {
4307    Query(Query<T>),
4308    DefaultValues,
4309}
4310
4311impl<T: AstInfo> AstDisplay for InsertSource<T> {
4312    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4313        match self {
4314            InsertSource::Query(query) => f.write_node(query),
4315            InsertSource::DefaultValues => f.write_str("DEFAULT VALUES"),
4316        }
4317    }
4318}
4319impl_display_t!(InsertSource);
4320
4321#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
4322pub enum ObjectType {
4323    Table,
4324    View,
4325    MaterializedView,
4326    Source,
4327    Sink,
4328    Index,
4329    Type,
4330    Role,
4331    Cluster,
4332    ClusterReplica,
4333    Secret,
4334    Connection,
4335    Database,
4336    Schema,
4337    Func,
4338    Subsource,
4339    NetworkPolicy,
4340}
4341
4342impl ObjectType {
4343    pub fn lives_in_schema(&self) -> bool {
4344        match self {
4345            ObjectType::Table
4346            | ObjectType::View
4347            | ObjectType::MaterializedView
4348            | ObjectType::Source
4349            | ObjectType::Sink
4350            | ObjectType::Index
4351            | ObjectType::Type
4352            | ObjectType::Secret
4353            | ObjectType::Connection
4354            | ObjectType::Func
4355            | ObjectType::Subsource => true,
4356            ObjectType::Database
4357            | ObjectType::Schema
4358            | ObjectType::Cluster
4359            | ObjectType::ClusterReplica
4360            | ObjectType::Role
4361            | ObjectType::NetworkPolicy => false,
4362        }
4363    }
4364}
4365
4366impl AstDisplay for ObjectType {
4367    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4368        f.write_str(match self {
4369            ObjectType::Table => "TABLE",
4370            ObjectType::View => "VIEW",
4371            ObjectType::MaterializedView => "MATERIALIZED VIEW",
4372            ObjectType::Source => "SOURCE",
4373            ObjectType::Sink => "SINK",
4374            ObjectType::Index => "INDEX",
4375            ObjectType::Type => "TYPE",
4376            ObjectType::Role => "ROLE",
4377            ObjectType::Cluster => "CLUSTER",
4378            ObjectType::ClusterReplica => "CLUSTER REPLICA",
4379            ObjectType::Secret => "SECRET",
4380            ObjectType::Connection => "CONNECTION",
4381            ObjectType::Database => "DATABASE",
4382            ObjectType::Schema => "SCHEMA",
4383            ObjectType::Func => "FUNCTION",
4384            ObjectType::Subsource => "SUBSOURCE",
4385            ObjectType::NetworkPolicy => "NETWORK POLICY",
4386        })
4387    }
4388}
4389impl_display!(ObjectType);
4390
4391#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
4392pub enum SystemObjectType {
4393    System,
4394    Object(ObjectType),
4395}
4396
4397impl AstDisplay for SystemObjectType {
4398    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4399        match self {
4400            SystemObjectType::System => f.write_str("SYSTEM"),
4401            SystemObjectType::Object(object) => f.write_node(object),
4402        }
4403    }
4404}
4405impl_display!(SystemObjectType);
4406
4407#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4408pub enum ShowStatementFilter<T: AstInfo> {
4409    Like(String),
4410    Where(Expr<T>),
4411}
4412
4413impl<T: AstInfo> AstDisplay for ShowStatementFilter<T> {
4414    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4415        use ShowStatementFilter::*;
4416        match self {
4417            Like(pattern) => {
4418                f.write_str("LIKE '");
4419                f.write_node(&display::escape_single_quote_string(pattern));
4420                f.write_str("'");
4421            }
4422            Where(expr) => {
4423                f.write_str("WHERE ");
4424                f.write_node(expr);
4425            }
4426        }
4427    }
4428}
4429impl_display_t!(ShowStatementFilter);
4430
4431#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4432pub enum WithOptionValue<T: AstInfo> {
4433    Value(Value),
4434    DataType(T::DataType),
4435    Secret(T::ItemName),
4436    Item(T::ItemName),
4437    UnresolvedItemName(UnresolvedItemName),
4438    Ident(Ident),
4439    Sequence(Vec<WithOptionValue<T>>),
4440    Map(BTreeMap<String, WithOptionValue<T>>),
4441    // Special cases.
4442    Expr(Expr<T>),
4443    ClusterReplicas(Vec<ReplicaDefinition<T>>),
4444    ConnectionKafkaBroker(KafkaBroker<T>),
4445    ConnectionAwsPrivatelink(ConnectionDefaultAwsPrivatelink<T>),
4446    KafkaMatchingBrokerRule(KafkaMatchingBrokerRule<T>),
4447    RetainHistoryFor(Value),
4448    Refresh(RefreshOptionValue<T>),
4449    ClusterScheduleOptionValue(ClusterScheduleOptionValue),
4450    ClusterAlterStrategy(ClusterAlterOptionValue<T>),
4451    NetworkPolicyRules(Vec<NetworkPolicyRuleDefinition<T>>),
4452}
4453
4454impl<T: AstInfo> AstDisplay for WithOptionValue<T> {
4455    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4456        if f.redacted() {
4457            // When adding branches to this match statement, think about whether it is OK for us to collect
4458            // the value as part of our telemetry. Check the data management policy to be sure!
4459            match self {
4460                WithOptionValue::Value(_)
4461                | WithOptionValue::Sequence(_)
4462                | WithOptionValue::Map(_)
4463                | WithOptionValue::RetainHistoryFor(_)
4464                | WithOptionValue::Refresh(_)
4465                | WithOptionValue::Expr(_) => {
4466                    // These are redact-aware.
4467                }
4468                WithOptionValue::ConnectionKafkaBroker(_) => {
4469                    f.write_str("'<REDACTED>'");
4470                    return;
4471                }
4472                // A secret reference is a catalog item name, not the secret
4473                // value, so it is safe to show in redacted output. An option
4474                // that accepts an inline credential is parsed as a `Value`,
4475                // which is redacted by that arm together with the option's
4476                // `redact_value()`.
4477                WithOptionValue::Secret(_) => {}
4478                WithOptionValue::DataType(_)
4479                | WithOptionValue::Item(_)
4480                | WithOptionValue::UnresolvedItemName(_)
4481                | WithOptionValue::Ident(_)
4482                | WithOptionValue::ConnectionAwsPrivatelink(_)
4483                | WithOptionValue::KafkaMatchingBrokerRule(_)
4484                | WithOptionValue::ClusterReplicas(_)
4485                | WithOptionValue::ClusterScheduleOptionValue(_)
4486                | WithOptionValue::ClusterAlterStrategy(_)
4487                | WithOptionValue::NetworkPolicyRules(_) => {
4488                    // These do not need redaction.
4489                }
4490            }
4491        }
4492        match self {
4493            WithOptionValue::Sequence(values) => {
4494                f.write_str("(");
4495                f.write_node(&display::comma_separated(values));
4496                f.write_str(")");
4497            }
4498            WithOptionValue::Map(values) => {
4499                f.write_str("MAP[");
4500                let len = values.len();
4501                for (i, (key, value)) in values.iter().enumerate() {
4502                    f.write_str("'");
4503                    f.write_node(&display::escape_single_quote_string(key));
4504                    f.write_str("' => ");
4505                    f.write_node(value);
4506                    if i + 1 < len {
4507                        f.write_str(", ");
4508                    }
4509                }
4510                f.write_str("]");
4511            }
4512            WithOptionValue::Expr(e) => f.write_node(e),
4513            WithOptionValue::Value(value) => f.write_node(value),
4514            WithOptionValue::DataType(typ) => f.write_node(typ),
4515            WithOptionValue::Secret(name) => {
4516                f.write_str("SECRET ");
4517                f.write_node(name)
4518            }
4519            WithOptionValue::Item(obj) => f.write_node(obj),
4520            WithOptionValue::UnresolvedItemName(r) => f.write_node(r),
4521            WithOptionValue::Ident(r) => f.write_node(r),
4522            WithOptionValue::ClusterReplicas(replicas) => {
4523                f.write_str("(");
4524                f.write_node(&display::comma_separated(replicas));
4525                f.write_str(")");
4526            }
4527            WithOptionValue::NetworkPolicyRules(rules) => {
4528                f.write_str("(");
4529                f.write_node(&display::comma_separated(rules));
4530                f.write_str(")");
4531            }
4532            WithOptionValue::ConnectionAwsPrivatelink(aws_privatelink) => {
4533                f.write_node(aws_privatelink);
4534            }
4535            WithOptionValue::KafkaMatchingBrokerRule(rule) => {
4536                f.write_node(rule);
4537            }
4538            WithOptionValue::ConnectionKafkaBroker(broker) => {
4539                f.write_node(broker);
4540            }
4541            WithOptionValue::RetainHistoryFor(value) => {
4542                f.write_str("FOR ");
4543                f.write_node(value);
4544            }
4545            WithOptionValue::Refresh(opt) => f.write_node(opt),
4546            WithOptionValue::ClusterScheduleOptionValue(value) => f.write_node(value),
4547            WithOptionValue::ClusterAlterStrategy(value) => f.write_node(value),
4548        }
4549    }
4550}
4551impl_display_t!(WithOptionValue);
4552
4553#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4554pub enum RefreshOptionValue<T: AstInfo> {
4555    OnCommit,
4556    AtCreation,
4557    At(RefreshAtOptionValue<T>),
4558    Every(RefreshEveryOptionValue<T>),
4559}
4560
4561#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4562pub struct RefreshAtOptionValue<T: AstInfo> {
4563    // We need an Expr because we want to support `mz_now()`.
4564    pub time: Expr<T>,
4565}
4566
4567#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4568pub struct RefreshEveryOptionValue<T: AstInfo> {
4569    // The refresh interval.
4570    pub interval: IntervalValue,
4571    // We need an Expr because we want to support `mz_now()`.
4572    pub aligned_to: Option<Expr<T>>,
4573}
4574
4575impl<T: AstInfo> AstDisplay for RefreshOptionValue<T> {
4576    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4577        match self {
4578            RefreshOptionValue::OnCommit => {
4579                f.write_str("ON COMMIT");
4580            }
4581            RefreshOptionValue::AtCreation => {
4582                f.write_str("AT CREATION");
4583            }
4584            RefreshOptionValue::At(RefreshAtOptionValue { time }) => {
4585                f.write_str("AT ");
4586                f.write_node(time);
4587            }
4588            RefreshOptionValue::Every(RefreshEveryOptionValue {
4589                interval,
4590                aligned_to,
4591            }) => {
4592                f.write_str("EVERY '");
4593                f.write_node(interval);
4594                if let Some(aligned_to) = aligned_to {
4595                    f.write_str(" ALIGNED TO ");
4596                    f.write_node(aligned_to)
4597                }
4598            }
4599        }
4600    }
4601}
4602
4603#[derive(
4604    Debug,
4605    Clone,
4606    PartialEq,
4607    Eq,
4608    Hash,
4609    PartialOrd,
4610    Ord,
4611    Deserialize,
4612    Serialize
4613)]
4614pub enum ClusterScheduleOptionValue {
4615    Manual,
4616    Refresh {
4617        hydration_time_estimate: Option<IntervalValue>,
4618    },
4619}
4620
4621impl Default for ClusterScheduleOptionValue {
4622    fn default() -> Self {
4623        // (Has to be consistent with `impl Default for ClusterSchedule`.)
4624        ClusterScheduleOptionValue::Manual
4625    }
4626}
4627
4628impl AstDisplay for ClusterScheduleOptionValue {
4629    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4630        match self {
4631            ClusterScheduleOptionValue::Manual => {
4632                f.write_str("MANUAL");
4633            }
4634            ClusterScheduleOptionValue::Refresh {
4635                hydration_time_estimate,
4636            } => {
4637                f.write_str("ON REFRESH");
4638                if let Some(hydration_time_estimate) = hydration_time_estimate {
4639                    f.write_str(" (HYDRATION TIME ESTIMATE = '");
4640                    f.write_node(hydration_time_estimate);
4641                    f.write_str(")");
4642                }
4643            }
4644        }
4645    }
4646}
4647
4648#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4649pub enum TransactionMode {
4650    AccessMode(TransactionAccessMode),
4651    IsolationLevel(TransactionIsolationLevel),
4652}
4653
4654impl AstDisplay for TransactionMode {
4655    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4656        use TransactionMode::*;
4657        match self {
4658            AccessMode(access_mode) => f.write_node(access_mode),
4659            IsolationLevel(iso_level) => {
4660                f.write_str("ISOLATION LEVEL ");
4661                f.write_node(iso_level);
4662            }
4663        }
4664    }
4665}
4666impl_display!(TransactionMode);
4667
4668/// The access mode of a transaction, as specified by the `BEGIN ...` statement.
4669#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4670pub enum TransactionAccessMode {
4671    ReadOnly,
4672    ReadWrite,
4673}
4674
4675impl AstDisplay for TransactionAccessMode {
4676    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4677        use TransactionAccessMode::*;
4678        f.write_str(match self {
4679            ReadOnly => "READ ONLY",
4680            ReadWrite => "READ WRITE",
4681        })
4682    }
4683}
4684impl_display!(TransactionAccessMode);
4685
4686#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
4687pub enum TransactionIsolationLevel {
4688    ReadUncommitted,
4689    ReadCommitted,
4690    RepeatableRead,
4691    Serializable,
4692    StrongSessionSerializable,
4693    StrictSerializable,
4694}
4695
4696impl AstDisplay for TransactionIsolationLevel {
4697    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4698        use TransactionIsolationLevel::*;
4699        f.write_str(match self {
4700            ReadUncommitted => "READ UNCOMMITTED",
4701            ReadCommitted => "READ COMMITTED",
4702            RepeatableRead => "REPEATABLE READ",
4703            Serializable => "SERIALIZABLE",
4704            StrongSessionSerializable => "STRONG SESSION SERIALIZABLE",
4705            StrictSerializable => "STRICT SERIALIZABLE",
4706        })
4707    }
4708}
4709impl_display!(TransactionIsolationLevel);
4710
4711#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4712pub enum SetVariableTo {
4713    Default,
4714    Values(Vec<SetVariableValue>),
4715}
4716
4717impl AstDisplay for SetVariableTo {
4718    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4719        use SetVariableTo::*;
4720        match self {
4721            Values(values) => f.write_node(&display::comma_separated(values)),
4722            Default => f.write_str("DEFAULT"),
4723        }
4724    }
4725}
4726impl_display!(SetVariableTo);
4727
4728#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4729pub enum SetVariableValue {
4730    Ident(Ident),
4731    Literal(Value),
4732}
4733
4734impl AstDisplay for SetVariableValue {
4735    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4736        use SetVariableValue::*;
4737        match self {
4738            Ident(ident) => f.write_node(ident),
4739            Literal(literal) => f.write_node(literal),
4740        }
4741    }
4742}
4743impl_display!(SetVariableValue);
4744
4745impl SetVariableValue {
4746    /// Returns the underlying value without quotes.
4747    pub fn into_unquoted_value(self) -> String {
4748        match self {
4749            // `lit.to_string` will quote a `Value::String`, so get the unquoted
4750            // version.
4751            SetVariableValue::Literal(Value::String(s)) => s,
4752            SetVariableValue::Literal(lit) => lit.to_string(),
4753            SetVariableValue::Ident(ident) => ident.into_string(),
4754        }
4755    }
4756}
4757
4758/// SQL assignment `foo = expr` as used in SQLUpdate
4759#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4760pub struct Assignment<T: AstInfo> {
4761    pub id: Ident,
4762    pub value: Expr<T>,
4763}
4764
4765impl<T: AstInfo> AstDisplay for Assignment<T> {
4766    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4767        f.write_node(&self.id);
4768        f.write_str(" = ");
4769        f.write_node(&self.value);
4770    }
4771}
4772impl_display_t!(Assignment);
4773
4774/// Specifies what [Statement::ExplainPlan] is actually explained.
4775#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4776pub enum ExplainStage {
4777    /// The mz_sql::HirRelationExpr after parsing
4778    RawPlan,
4779    /// The mz_expr::MirRelationExpr after decorrelation
4780    DecorrelatedPlan,
4781    /// The mz_expr::MirRelationExpr after local optimization
4782    LocalPlan,
4783    /// The mz_expr::MirRelationExpr after global optimization
4784    GlobalPlan,
4785    /// The mz_compute_types::plan::Plan
4786    PhysicalPlan,
4787    /// The complete trace of the plan through the optimizer
4788    Trace,
4789    /// Insights about the plan
4790    PlanInsights,
4791}
4792
4793impl ExplainStage {
4794    /// Return the tracing path that corresponds to a given stage.
4795    pub fn paths(&self) -> Option<SmallVec<[NamedPlan; 4]>> {
4796        use NamedPlan::*;
4797        match self {
4798            Self::RawPlan => Some(smallvec![Raw]),
4799            Self::DecorrelatedPlan => Some(smallvec![Decorrelated]),
4800            Self::LocalPlan => Some(smallvec![Local]),
4801            Self::GlobalPlan => Some(smallvec![Global]),
4802            Self::PhysicalPlan => Some(smallvec![Physical]),
4803            Self::Trace => None,
4804            Self::PlanInsights => Some(smallvec![Raw, Global, FastPath]),
4805        }
4806    }
4807
4808    // Whether instead of the plan associated with this [`ExplainStage`] we
4809    // should show the [`NamedPlan::FastPath`] plan if available.
4810    pub fn show_fast_path(&self) -> bool {
4811        match self {
4812            Self::RawPlan => false,
4813            Self::DecorrelatedPlan => false,
4814            Self::LocalPlan => false,
4815            Self::GlobalPlan => true,
4816            Self::PhysicalPlan => true,
4817            Self::Trace => false,
4818            Self::PlanInsights => false,
4819        }
4820    }
4821}
4822
4823impl AstDisplay for ExplainStage {
4824    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4825        match self {
4826            Self::RawPlan => f.write_str("RAW PLAN"),
4827            Self::DecorrelatedPlan => f.write_str("DECORRELATED PLAN"),
4828            Self::LocalPlan => f.write_str("LOCALLY OPTIMIZED PLAN"),
4829            Self::GlobalPlan => f.write_str("OPTIMIZED PLAN"),
4830            Self::PhysicalPlan => f.write_str("PHYSICAL PLAN"),
4831            Self::Trace => f.write_str("OPTIMIZER TRACE"),
4832            Self::PlanInsights => f.write_str("PLAN INSIGHTS"),
4833        }
4834    }
4835}
4836impl_display!(ExplainStage);
4837
4838/// An enum of named plans that identifies specific stages in an optimizer trace
4839/// where these plans can be found.
4840#[derive(Clone)]
4841pub enum NamedPlan {
4842    Raw,
4843    Decorrelated,
4844    Local,
4845    Global,
4846    Physical,
4847    FastPath,
4848}
4849
4850impl NamedPlan {
4851    /// Return the [`NamedPlan`] for a given `path` if it exists.
4852    pub fn of_path(value: &str) -> Option<Self> {
4853        match value {
4854            "optimize/raw" => Some(Self::Raw),
4855            "optimize/hir_to_mir" => Some(Self::Decorrelated),
4856            "optimize/local" => Some(Self::Local),
4857            "optimize/global" => Some(Self::Global),
4858            "optimize/finalize_dataflow" => Some(Self::Physical),
4859            "optimize/fast_path" => Some(Self::FastPath),
4860            _ => None,
4861        }
4862    }
4863
4864    /// Return the tracing path under which the plan can be found in an
4865    /// optimizer trace.
4866    pub fn path(&self) -> &'static str {
4867        match self {
4868            Self::Raw => "optimize/raw",
4869            Self::Decorrelated => "optimize/hir_to_mir",
4870            Self::Local => "optimize/local",
4871            Self::Global => "optimize/global",
4872            Self::Physical => "optimize/finalize_dataflow",
4873            Self::FastPath => "optimize/fast_path",
4874        }
4875    }
4876}
4877
4878/// What is being explained.
4879/// The bools mean whether this is an EXPLAIN BROKEN.
4880#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4881pub enum Explainee<T: AstInfo> {
4882    View(T::ItemName),
4883    MaterializedView(T::ItemName),
4884    Index(T::ItemName),
4885    ReplanView(T::ItemName),
4886    ReplanMaterializedView(T::ItemName),
4887    ReplanIndex(T::ItemName),
4888    Select(Box<SelectStatement<T>>, bool),
4889    CreateView(Box<CreateViewStatement<T>>, bool),
4890    CreateMaterializedView(Box<CreateMaterializedViewStatement<T>>, bool),
4891    CreateIndex(Box<CreateIndexStatement<T>>, bool),
4892    Subscribe(Box<SubscribeStatement<T>>, bool),
4893}
4894
4895impl<T: AstInfo> Explainee<T> {
4896    pub fn name(&self) -> Option<&T::ItemName> {
4897        match self {
4898            Self::View(name)
4899            | Self::ReplanView(name)
4900            | Self::MaterializedView(name)
4901            | Self::ReplanMaterializedView(name)
4902            | Self::Index(name)
4903            | Self::ReplanIndex(name) => Some(name),
4904            Self::Select(..)
4905            | Self::CreateView(..)
4906            | Self::CreateMaterializedView(..)
4907            | Self::CreateIndex(..)
4908            | Self::Subscribe(..) => None,
4909        }
4910    }
4911
4912    pub fn is_view(&self) -> bool {
4913        use Explainee::*;
4914        matches!(self, View(_) | ReplanView(_) | CreateView(_, _))
4915    }
4916}
4917
4918impl<T: AstInfo> AstDisplay for Explainee<T> {
4919    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4920        match self {
4921            Self::View(name) => {
4922                f.write_str("VIEW ");
4923                f.write_node(name);
4924            }
4925            Self::MaterializedView(name) => {
4926                f.write_str("MATERIALIZED VIEW ");
4927                f.write_node(name);
4928            }
4929            Self::Index(name) => {
4930                f.write_str("INDEX ");
4931                f.write_node(name);
4932            }
4933            Self::ReplanView(name) => {
4934                f.write_str("REPLAN VIEW ");
4935                f.write_node(name);
4936            }
4937            Self::ReplanMaterializedView(name) => {
4938                f.write_str("REPLAN MATERIALIZED VIEW ");
4939                f.write_node(name);
4940            }
4941            Self::ReplanIndex(name) => {
4942                f.write_str("REPLAN INDEX ");
4943                f.write_node(name);
4944            }
4945            Self::Select(select, broken) => {
4946                if *broken {
4947                    f.write_str("BROKEN ");
4948                }
4949                f.write_node(select);
4950            }
4951            Self::CreateView(statement, broken) => {
4952                if *broken {
4953                    f.write_str("BROKEN ");
4954                }
4955                f.write_node(statement);
4956            }
4957            Self::CreateMaterializedView(statement, broken) => {
4958                if *broken {
4959                    f.write_str("BROKEN ");
4960                }
4961                f.write_node(statement);
4962            }
4963            Self::CreateIndex(statement, broken) => {
4964                if *broken {
4965                    f.write_str("BROKEN ");
4966                }
4967                f.write_node(statement);
4968            }
4969            Self::Subscribe(statement, broken) => {
4970                if *broken {
4971                    f.write_str("BROKEN ");
4972                }
4973                f.write_node(statement);
4974            }
4975        }
4976    }
4977}
4978impl_display_t!(Explainee);
4979
4980#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
4981pub enum ExplainFormat {
4982    /// Human readable display format
4983    Text,
4984    /// Human readable display format with full debug information
4985    VerboseText,
4986    /// Machine-consumable JSON format
4987    Json,
4988    /// Machine-consumable DOT (graphviz) format
4989    Dot,
4990}
4991
4992impl AstDisplay for ExplainFormat {
4993    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
4994        match self {
4995            Self::Text => f.write_str("TEXT"),
4996            Self::VerboseText => f.write_str("VERBOSE TEXT"),
4997            Self::Json => f.write_str("JSON"),
4998            Self::Dot => f.write_str("DOT"),
4999        }
5000    }
5001}
5002impl_display!(ExplainFormat);
5003
5004#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5005pub enum IfExistsBehavior {
5006    Error,
5007    Skip,
5008    Replace,
5009}
5010
5011/// `DECLARE ...`
5012#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5013pub struct DeclareStatement<T: AstInfo> {
5014    pub name: Ident,
5015    pub stmt: Box<T::NestedStatement>,
5016    pub sql: String,
5017}
5018
5019impl<T: AstInfo> AstDisplay for DeclareStatement<T> {
5020    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5021        f.write_str("DECLARE ");
5022        f.write_node(&self.name);
5023        f.write_str(" CURSOR FOR ");
5024        f.write_node(&self.stmt);
5025    }
5026}
5027impl_display_t!(DeclareStatement);
5028
5029/// `CLOSE ...`
5030#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5031pub struct CloseStatement {
5032    pub name: Ident,
5033}
5034
5035impl AstDisplay for CloseStatement {
5036    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5037        f.write_str("CLOSE ");
5038        f.write_node(&self.name);
5039    }
5040}
5041impl_display!(CloseStatement);
5042
5043#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5044pub enum FetchOptionName {
5045    Timeout,
5046}
5047
5048impl AstDisplay for FetchOptionName {
5049    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5050        f.write_str(match self {
5051            FetchOptionName::Timeout => "TIMEOUT",
5052        })
5053    }
5054}
5055
5056impl WithOptionName for FetchOptionName {
5057    /// # WARNING
5058    ///
5059    /// Whenever implementing this trait consider very carefully whether or not
5060    /// this value could contain sensitive user data. If you're uncertain, err
5061    /// on the conservative side and return `true`.
5062    fn redact_value(&self) -> bool {
5063        match self {
5064            FetchOptionName::Timeout => false,
5065        }
5066    }
5067}
5068
5069#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5070pub struct FetchOption<T: AstInfo> {
5071    pub name: FetchOptionName,
5072    pub value: Option<WithOptionValue<T>>,
5073}
5074impl_display_for_with_option!(FetchOption);
5075
5076/// `FETCH ...`
5077#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5078pub struct FetchStatement<T: AstInfo> {
5079    pub name: Ident,
5080    pub count: Option<FetchDirection>,
5081    pub options: Vec<FetchOption<T>>,
5082}
5083
5084impl<T: AstInfo> AstDisplay for FetchStatement<T> {
5085    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5086        f.write_str("FETCH ");
5087        if let Some(ref count) = self.count {
5088            f.write_str(format!("{} ", count));
5089        }
5090        // `FETCH` consumes an optional leading `FORWARD` keyword, so a cursor
5091        // literally named `forward` printed bare with no preceding count would
5092        // be swallowed on reparse, leaving no cursor name. Force it to quote.
5093        if self.count.is_none() && self.name.as_str().eq_ignore_ascii_case("forward") {
5094            f.write_str(self.name.to_ast_string_stable());
5095        } else {
5096            f.write_node(&self.name);
5097        }
5098        if !self.options.is_empty() {
5099            f.write_str(" WITH (");
5100            f.write_node(&display::comma_separated(&self.options));
5101            f.write_str(")");
5102        }
5103    }
5104}
5105impl_display_t!(FetchStatement);
5106
5107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5108pub enum FetchDirection {
5109    ForwardAll,
5110    ForwardCount(u64),
5111}
5112
5113impl AstDisplay for FetchDirection {
5114    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5115        match self {
5116            FetchDirection::ForwardAll => f.write_str("ALL"),
5117            FetchDirection::ForwardCount(count) => f.write_str(format!("{}", count)),
5118        }
5119    }
5120}
5121impl_display!(FetchDirection);
5122
5123/// `PREPARE ...`
5124#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5125pub struct PrepareStatement<T: AstInfo> {
5126    pub name: Ident,
5127    pub stmt: Box<T::NestedStatement>,
5128    pub sql: String,
5129}
5130
5131impl<T: AstInfo> AstDisplay for PrepareStatement<T> {
5132    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5133        f.write_str("PREPARE ");
5134        f.write_node(&self.name);
5135        f.write_str(" AS ");
5136        f.write_node(&self.stmt);
5137    }
5138}
5139impl_display_t!(PrepareStatement);
5140
5141/// `EXECUTE ...`
5142#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5143pub struct ExecuteStatement<T: AstInfo> {
5144    pub name: Ident,
5145    pub params: Vec<Expr<T>>,
5146}
5147
5148impl<T: AstInfo> AstDisplay for ExecuteStatement<T> {
5149    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5150        f.write_str("EXECUTE ");
5151        f.write_node(&self.name);
5152        if !self.params.is_empty() {
5153            f.write_str(" (");
5154            f.write_node(&display::comma_separated(&self.params));
5155            f.write_str(")");
5156        }
5157    }
5158}
5159impl_display_t!(ExecuteStatement);
5160
5161/// `EXECUTE UNIT TEST ...`
5162#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5163pub struct ExecuteUnitTestStatement<T: AstInfo> {
5164    pub name: Ident,
5165    pub target: T::ItemName,
5166    pub at_time: Option<Expr<T>>,
5167    pub mocks: Vec<MockViewDef<T>>,
5168    pub expected: ExpectedResultDef<T>,
5169}
5170
5171impl<T: AstInfo> AstDisplay for ExecuteUnitTestStatement<T> {
5172    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5173        f.write_str("EXECUTE UNIT TEST ");
5174        f.write_node(&self.name);
5175        f.write_str(" FOR ");
5176        f.write_node(&self.target);
5177        if let Some(at_time) = &self.at_time {
5178            f.write_str(" AT TIME ");
5179            f.write_node(at_time);
5180        }
5181        for (i, mock) in self.mocks.iter().enumerate() {
5182            f.write_str(if i == 0 { " MOCK " } else { ", MOCK " });
5183            f.write_node(mock);
5184        }
5185        f.write_str(" EXPECTED ");
5186        f.write_node(&self.expected);
5187    }
5188}
5189impl_display_t!(ExecuteUnitTestStatement);
5190
5191/// Mock view definition for EXECUTE UNIT TEST
5192#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5193pub struct MockViewDef<T: AstInfo> {
5194    pub name: T::ItemName,
5195    pub columns: Vec<ColumnDef<T>>,
5196    pub query: Query<T>,
5197}
5198
5199impl<T: AstInfo> AstDisplay for MockViewDef<T> {
5200    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5201        f.write_node(&self.name);
5202        f.write_str("(");
5203        f.write_node(&display::comma_separated(&self.columns));
5204        f.write_str(") AS (");
5205        f.write_node(&self.query);
5206        f.write_str(")");
5207    }
5208}
5209impl_display_t!(MockViewDef);
5210
5211/// Expected result definition for EXECUTE UNIT TEST
5212#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5213pub struct ExpectedResultDef<T: AstInfo> {
5214    pub columns: Vec<ColumnDef<T>>,
5215    pub query: Query<T>,
5216}
5217
5218impl<T: AstInfo> AstDisplay for ExpectedResultDef<T> {
5219    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5220        f.write_str("(");
5221        f.write_node(&display::comma_separated(&self.columns));
5222        f.write_str(") AS (");
5223        f.write_node(&self.query);
5224        f.write_str(")");
5225    }
5226}
5227impl_display_t!(ExpectedResultDef);
5228
5229/// `DEALLOCATE ...`
5230#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5231pub struct DeallocateStatement {
5232    pub name: Option<Ident>,
5233}
5234
5235impl AstDisplay for DeallocateStatement {
5236    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5237        f.write_str("DEALLOCATE ");
5238        match &self.name {
5239            Some(name) => f.write_node(name),
5240            None => f.write_str("ALL"),
5241        };
5242    }
5243}
5244impl_display!(DeallocateStatement);
5245
5246/// `RAISE ...`
5247#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5248pub struct RaiseStatement {
5249    pub severity: NoticeSeverity,
5250}
5251
5252impl AstDisplay for RaiseStatement {
5253    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5254        f.write_str("RAISE ");
5255        f.write_node(&self.severity);
5256    }
5257}
5258impl_display!(RaiseStatement);
5259
5260#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5261pub enum NoticeSeverity {
5262    Debug,
5263    Info,
5264    Log,
5265    Notice,
5266    Warning,
5267}
5268
5269impl AstDisplay for NoticeSeverity {
5270    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5271        f.write_str(match self {
5272            NoticeSeverity::Debug => "DEBUG",
5273            NoticeSeverity::Info => "INFO",
5274            NoticeSeverity::Log => "LOG",
5275            NoticeSeverity::Notice => "NOTICE",
5276            NoticeSeverity::Warning => "WARNING",
5277        })
5278    }
5279}
5280impl_display!(NoticeSeverity);
5281
5282/// `ALTER SYSTEM SET ...`
5283#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5284pub struct AlterSystemSetStatement {
5285    pub name: Ident,
5286    pub to: SetVariableTo,
5287}
5288
5289impl AstDisplay for AlterSystemSetStatement {
5290    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5291        f.write_str("ALTER SYSTEM SET ");
5292        f.write_node(&self.name);
5293        f.write_str(" = ");
5294        f.write_node(&self.to);
5295    }
5296}
5297impl_display!(AlterSystemSetStatement);
5298
5299/// `ALTER SYSTEM RESET ...`
5300#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5301pub struct AlterSystemResetStatement {
5302    pub name: Ident,
5303}
5304
5305impl AstDisplay for AlterSystemResetStatement {
5306    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5307        f.write_str("ALTER SYSTEM RESET ");
5308        f.write_node(&self.name);
5309    }
5310}
5311impl_display!(AlterSystemResetStatement);
5312
5313/// `ALTER SYSTEM RESET ALL`
5314#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5315pub struct AlterSystemResetAllStatement {}
5316
5317impl AstDisplay for AlterSystemResetAllStatement {
5318    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5319        f.write_str("ALTER SYSTEM RESET ALL");
5320    }
5321}
5322impl_display!(AlterSystemResetAllStatement);
5323
5324#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5325pub enum AsOf<T: AstInfo> {
5326    At(Expr<T>),
5327    AtLeast(Expr<T>),
5328}
5329
5330impl<T: AstInfo> AstDisplay for AsOf<T> {
5331    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5332        f.write_str("AS OF ");
5333        match self {
5334            AsOf::At(expr) => f.write_node(expr),
5335            AsOf::AtLeast(expr) => {
5336                f.write_str("AT LEAST ");
5337                f.write_node(expr);
5338            }
5339        }
5340    }
5341}
5342impl_display_t!(AsOf);
5343
5344#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
5345pub enum ShowStatement<T: AstInfo> {
5346    ShowObjects(ShowObjectsStatement<T>),
5347    ShowColumns(ShowColumnsStatement<T>),
5348    ShowCreateView(ShowCreateViewStatement<T>),
5349    ShowCreateMaterializedView(ShowCreateMaterializedViewStatement<T>),
5350    ShowCreateSource(ShowCreateSourceStatement<T>),
5351    ShowCreateTable(ShowCreateTableStatement<T>),
5352    ShowCreateSink(ShowCreateSinkStatement<T>),
5353    ShowCreateIndex(ShowCreateIndexStatement<T>),
5354    ShowCreateConnection(ShowCreateConnectionStatement<T>),
5355    ShowCreateCluster(ShowCreateClusterStatement<T>),
5356    ShowCreateType(ShowCreateTypeStatement<T>),
5357    ShowVariable(ShowVariableStatement),
5358    InspectShard(InspectShardStatement),
5359}
5360
5361impl<T: AstInfo> AstDisplay for ShowStatement<T> {
5362    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5363        match self {
5364            ShowStatement::ShowObjects(stmt) => f.write_node(stmt),
5365            ShowStatement::ShowColumns(stmt) => f.write_node(stmt),
5366            ShowStatement::ShowCreateView(stmt) => f.write_node(stmt),
5367            ShowStatement::ShowCreateMaterializedView(stmt) => f.write_node(stmt),
5368            ShowStatement::ShowCreateSource(stmt) => f.write_node(stmt),
5369            ShowStatement::ShowCreateTable(stmt) => f.write_node(stmt),
5370            ShowStatement::ShowCreateSink(stmt) => f.write_node(stmt),
5371            ShowStatement::ShowCreateIndex(stmt) => f.write_node(stmt),
5372            ShowStatement::ShowCreateConnection(stmt) => f.write_node(stmt),
5373            ShowStatement::ShowCreateCluster(stmt) => f.write_node(stmt),
5374            ShowStatement::ShowCreateType(stmt) => f.write_node(stmt),
5375            ShowStatement::ShowVariable(stmt) => f.write_node(stmt),
5376            ShowStatement::InspectShard(stmt) => f.write_node(stmt),
5377        }
5378    }
5379}
5380impl_display_t!(ShowStatement);
5381
5382/// `GRANT ...`
5383#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5384pub struct GrantRoleStatement<T: AstInfo> {
5385    /// The roles that are gaining members.
5386    pub role_names: Vec<T::RoleName>,
5387    /// The roles that will be added to `role_name`.
5388    pub member_names: Vec<T::RoleName>,
5389}
5390
5391impl<T: AstInfo> AstDisplay for GrantRoleStatement<T> {
5392    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5393        f.write_str("GRANT ");
5394        f.write_node(&display::comma_separated(&self.role_names));
5395        f.write_str(" TO ");
5396        f.write_node(&display::comma_separated(&self.member_names));
5397    }
5398}
5399impl_display_t!(GrantRoleStatement);
5400
5401/// `REVOKE ...`
5402#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5403pub struct RevokeRoleStatement<T: AstInfo> {
5404    /// The roles that are losing members.
5405    pub role_names: Vec<T::RoleName>,
5406    /// The roles that will be removed from `role_name`.
5407    pub member_names: Vec<T::RoleName>,
5408}
5409
5410impl<T: AstInfo> AstDisplay for RevokeRoleStatement<T> {
5411    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5412        f.write_str("REVOKE ");
5413        f.write_node(&display::comma_separated(&self.role_names));
5414        f.write_str(" FROM ");
5415        f.write_node(&display::comma_separated(&self.member_names));
5416    }
5417}
5418impl_display_t!(RevokeRoleStatement);
5419
5420#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5421pub enum Privilege {
5422    SELECT,
5423    INSERT,
5424    UPDATE,
5425    DELETE,
5426    USAGE,
5427    CREATE,
5428    CREATEROLE,
5429    CREATEDB,
5430    CREATECLUSTER,
5431    CREATENETWORKPOLICY,
5432}
5433
5434impl AstDisplay for Privilege {
5435    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5436        f.write_str(match self {
5437            Privilege::SELECT => "SELECT",
5438            Privilege::INSERT => "INSERT",
5439            Privilege::UPDATE => "UPDATE",
5440            Privilege::DELETE => "DELETE",
5441            Privilege::CREATE => "CREATE",
5442            Privilege::USAGE => "USAGE",
5443            Privilege::CREATEROLE => "CREATEROLE",
5444            Privilege::CREATEDB => "CREATEDB",
5445            Privilege::CREATECLUSTER => "CREATECLUSTER",
5446            Privilege::CREATENETWORKPOLICY => "CREATENETWORKPOLICY",
5447        });
5448    }
5449}
5450impl_display!(Privilege);
5451
5452#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5453pub enum PrivilegeSpecification {
5454    All,
5455    Privileges(Vec<Privilege>),
5456}
5457
5458impl AstDisplay for PrivilegeSpecification {
5459    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5460        match self {
5461            PrivilegeSpecification::All => f.write_str("ALL"),
5462            PrivilegeSpecification::Privileges(privileges) => {
5463                f.write_node(&display::comma_separated(privileges))
5464            }
5465        }
5466    }
5467}
5468impl_display!(PrivilegeSpecification);
5469
5470#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5471pub enum GrantTargetSpecification<T: AstInfo> {
5472    Object {
5473        /// The type of object.
5474        ///
5475        /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5476        object_type: ObjectType,
5477        /// Specification of each object affected.
5478        object_spec_inner: GrantTargetSpecificationInner<T>,
5479    },
5480    System,
5481}
5482
5483#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5484pub enum GrantTargetSpecificationInner<T: AstInfo> {
5485    All(GrantTargetAllSpecification<T>),
5486    Objects { names: Vec<T::ObjectName> },
5487}
5488
5489#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5490pub enum GrantTargetAllSpecification<T: AstInfo> {
5491    All,
5492    AllDatabases { databases: Vec<T::DatabaseName> },
5493    AllSchemas { schemas: Vec<T::SchemaName> },
5494}
5495
5496impl<T: AstInfo> GrantTargetAllSpecification<T> {
5497    pub fn len(&self) -> usize {
5498        match self {
5499            GrantTargetAllSpecification::All => 1,
5500            GrantTargetAllSpecification::AllDatabases { databases } => databases.len(),
5501            GrantTargetAllSpecification::AllSchemas { schemas } => schemas.len(),
5502        }
5503    }
5504}
5505
5506/// Writes the plural keyword for `object_type` as `GRANT`/`REVOKE ... ON ALL`
5507/// expects it. Most object types just take a trailing `S` (`TABLES`, `SECRETS`,
5508/// ...), but `NETWORK POLICY` pluralizes to the `POLICIES` keyword the parser
5509/// accepts — naively appending `S` would emit `NETWORK POLICYS`, which fails to
5510/// reparse.
5511fn write_grant_object_type_plural<W: fmt::Write>(
5512    f: &mut AstFormatter<W>,
5513    object_type: &ObjectType,
5514) {
5515    match object_type {
5516        ObjectType::NetworkPolicy => f.write_str("POLICIES"),
5517        other => {
5518            f.write_node(other);
5519            f.write_str("S");
5520        }
5521    }
5522}
5523
5524impl<T: AstInfo> AstDisplay for GrantTargetSpecification<T> {
5525    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5526        match self {
5527            GrantTargetSpecification::Object {
5528                object_type,
5529                object_spec_inner,
5530            } => match object_spec_inner {
5531                GrantTargetSpecificationInner::All(all_spec) => match all_spec {
5532                    GrantTargetAllSpecification::All => {
5533                        f.write_str("ALL ");
5534                        write_grant_object_type_plural(f, object_type);
5535                    }
5536                    GrantTargetAllSpecification::AllDatabases { databases } => {
5537                        f.write_str("ALL ");
5538                        write_grant_object_type_plural(f, object_type);
5539                        f.write_str(" IN DATABASE ");
5540                        f.write_node(&display::comma_separated(databases));
5541                    }
5542                    GrantTargetAllSpecification::AllSchemas { schemas } => {
5543                        f.write_str("ALL ");
5544                        write_grant_object_type_plural(f, object_type);
5545                        f.write_str(" IN SCHEMA ");
5546                        f.write_node(&display::comma_separated(schemas));
5547                    }
5548                },
5549                GrantTargetSpecificationInner::Objects { names } => {
5550                    f.write_node(object_type);
5551                    f.write_str(" ");
5552                    f.write_node(&display::comma_separated(names));
5553                }
5554            },
5555            GrantTargetSpecification::System => f.write_str("SYSTEM"),
5556        }
5557    }
5558}
5559impl_display_t!(GrantTargetSpecification);
5560
5561/// `GRANT ...`
5562#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5563pub struct GrantPrivilegesStatement<T: AstInfo> {
5564    /// The privileges being granted on an object.
5565    pub privileges: PrivilegeSpecification,
5566    /// The objects that are affected by the GRANT.
5567    pub target: GrantTargetSpecification<T>,
5568    /// The roles that will granted the privileges.
5569    pub roles: Vec<T::RoleName>,
5570}
5571
5572impl<T: AstInfo> AstDisplay for GrantPrivilegesStatement<T> {
5573    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5574        f.write_str("GRANT ");
5575        f.write_node(&self.privileges);
5576        f.write_str(" ON ");
5577        f.write_node(&self.target);
5578        f.write_str(" TO ");
5579        f.write_node(&display::comma_separated(&self.roles));
5580    }
5581}
5582impl_display_t!(GrantPrivilegesStatement);
5583
5584/// `REVOKE ...`
5585#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5586pub struct RevokePrivilegesStatement<T: AstInfo> {
5587    /// The privileges being revoked.
5588    pub privileges: PrivilegeSpecification,
5589    /// The objects that are affected by the REVOKE.
5590    pub target: GrantTargetSpecification<T>,
5591    /// The roles that will have privileges revoked.
5592    pub roles: Vec<T::RoleName>,
5593}
5594
5595impl<T: AstInfo> AstDisplay for RevokePrivilegesStatement<T> {
5596    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5597        f.write_str("REVOKE ");
5598        f.write_node(&self.privileges);
5599        f.write_str(" ON ");
5600        f.write_node(&self.target);
5601        f.write_str(" FROM ");
5602        f.write_node(&display::comma_separated(&self.roles));
5603    }
5604}
5605impl_display_t!(RevokePrivilegesStatement);
5606
5607#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5608pub enum TargetRoleSpecification<T: AstInfo> {
5609    /// Specific list of roles.
5610    Roles(Vec<T::RoleName>),
5611    /// All current and future roles.
5612    AllRoles,
5613}
5614
5615impl<T: AstInfo> AstDisplay for TargetRoleSpecification<T> {
5616    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5617        match self {
5618            TargetRoleSpecification::Roles(roles) => f.write_node(&display::comma_separated(roles)),
5619            TargetRoleSpecification::AllRoles => f.write_str("ALL ROLES"),
5620        }
5621    }
5622}
5623impl_display_t!(TargetRoleSpecification);
5624
5625#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5626pub struct AbbreviatedGrantStatement<T: AstInfo> {
5627    /// The privileges being granted.
5628    pub privileges: PrivilegeSpecification,
5629    /// The type of object.
5630    ///
5631    /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5632    pub object_type: ObjectType,
5633    /// The roles that will granted the privileges.
5634    pub grantees: Vec<T::RoleName>,
5635}
5636
5637impl<T: AstInfo> AstDisplay for AbbreviatedGrantStatement<T> {
5638    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5639        f.write_str("GRANT ");
5640        f.write_node(&self.privileges);
5641        f.write_str(" ON ");
5642        f.write_node(&self.object_type);
5643        f.write_str("S TO ");
5644        f.write_node(&display::comma_separated(&self.grantees));
5645    }
5646}
5647impl_display_t!(AbbreviatedGrantStatement);
5648
5649#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5650pub struct AbbreviatedRevokeStatement<T: AstInfo> {
5651    /// The privileges being revoked.
5652    pub privileges: PrivilegeSpecification,
5653    /// The type of object.
5654    ///
5655    /// Note: For views, materialized views, and sources this will be [`ObjectType::Table`].
5656    pub object_type: ObjectType,
5657    /// The roles that the privilege will be revoked from.
5658    pub revokees: Vec<T::RoleName>,
5659}
5660
5661impl<T: AstInfo> AstDisplay for AbbreviatedRevokeStatement<T> {
5662    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5663        f.write_str("REVOKE ");
5664        f.write_node(&self.privileges);
5665        f.write_str(" ON ");
5666        f.write_node(&self.object_type);
5667        f.write_str("S FROM ");
5668        f.write_node(&display::comma_separated(&self.revokees));
5669    }
5670}
5671impl_display_t!(AbbreviatedRevokeStatement);
5672
5673#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5674pub enum AbbreviatedGrantOrRevokeStatement<T: AstInfo> {
5675    Grant(AbbreviatedGrantStatement<T>),
5676    Revoke(AbbreviatedRevokeStatement<T>),
5677}
5678
5679impl<T: AstInfo> AbbreviatedGrantOrRevokeStatement<T> {
5680    pub fn privileges(&self) -> &PrivilegeSpecification {
5681        match self {
5682            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.privileges,
5683            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.privileges,
5684        }
5685    }
5686
5687    pub fn object_type(&self) -> &ObjectType {
5688        match self {
5689            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.object_type,
5690            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.object_type,
5691        }
5692    }
5693
5694    pub fn roles(&self) -> &Vec<T::RoleName> {
5695        match self {
5696            AbbreviatedGrantOrRevokeStatement::Grant(grant) => &grant.grantees,
5697            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => &revoke.revokees,
5698        }
5699    }
5700}
5701
5702impl<T: AstInfo> AstDisplay for AbbreviatedGrantOrRevokeStatement<T> {
5703    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5704        match self {
5705            AbbreviatedGrantOrRevokeStatement::Grant(grant) => f.write_node(grant),
5706            AbbreviatedGrantOrRevokeStatement::Revoke(revoke) => f.write_node(revoke),
5707        }
5708    }
5709}
5710impl_display_t!(AbbreviatedGrantOrRevokeStatement);
5711
5712/// `ALTER DEFAULT PRIVILEGES ...`
5713#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5714pub struct AlterDefaultPrivilegesStatement<T: AstInfo> {
5715    /// The roles for which created objects are affected.
5716    pub target_roles: TargetRoleSpecification<T>,
5717    /// The objects that are affected by the default privilege.
5718    pub target_objects: GrantTargetAllSpecification<T>,
5719    /// The privilege to grant or revoke.
5720    pub grant_or_revoke: AbbreviatedGrantOrRevokeStatement<T>,
5721}
5722
5723impl<T: AstInfo> AstDisplay for AlterDefaultPrivilegesStatement<T> {
5724    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5725        f.write_str("ALTER DEFAULT PRIVILEGES");
5726        match &self.target_roles {
5727            TargetRoleSpecification::Roles(_) => {
5728                f.write_str(" FOR ROLE ");
5729                f.write_node(&self.target_roles);
5730            }
5731            TargetRoleSpecification::AllRoles => {
5732                f.write_str(" FOR ");
5733                f.write_node(&self.target_roles);
5734            }
5735        }
5736        match &self.target_objects {
5737            GrantTargetAllSpecification::All => {}
5738            GrantTargetAllSpecification::AllDatabases { databases } => {
5739                f.write_str(" IN DATABASE ");
5740                f.write_node(&display::comma_separated(databases));
5741            }
5742            GrantTargetAllSpecification::AllSchemas { schemas } => {
5743                f.write_str(" IN SCHEMA ");
5744                f.write_node(&display::comma_separated(schemas));
5745            }
5746        }
5747        f.write_str(" ");
5748        f.write_node(&self.grant_or_revoke);
5749    }
5750}
5751impl_display_t!(AlterDefaultPrivilegesStatement);
5752
5753/// `REASSIGN OWNED ...`
5754#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5755pub struct ReassignOwnedStatement<T: AstInfo> {
5756    /// The roles whose owned objects are being reassigned.
5757    pub old_roles: Vec<T::RoleName>,
5758    /// The new owner of the objects.
5759    pub new_role: T::RoleName,
5760}
5761
5762impl<T: AstInfo> AstDisplay for ReassignOwnedStatement<T> {
5763    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5764        f.write_str("REASSIGN OWNED BY ");
5765        f.write_node(&display::comma_separated(&self.old_roles));
5766        f.write_str(" TO ");
5767        f.write_node(&self.new_role);
5768    }
5769}
5770impl_display_t!(ReassignOwnedStatement);
5771
5772/// `COMMENT ON ...`
5773#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5774pub struct CommentStatement<T: AstInfo> {
5775    pub object: CommentObjectType<T>,
5776    pub comment: Option<String>,
5777}
5778
5779impl<T: AstInfo> AstDisplay for CommentStatement<T> {
5780    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5781        f.write_str("COMMENT ON ");
5782        f.write_node(&self.object);
5783
5784        f.write_str(" IS ");
5785        match &self.comment {
5786            Some(s) => {
5787                if f.redacted() {
5788                    // The comment body is arbitrary free text and may contain PII,
5789                    // so redact it like every other user-supplied value.
5790                    f.write_str("'<REDACTED>'");
5791                } else {
5792                    f.write_str("'");
5793                    f.write_node(&display::escape_single_quote_string(s));
5794                    f.write_str("'");
5795                }
5796            }
5797            None => f.write_str("NULL"),
5798        }
5799    }
5800}
5801impl_display_t!(CommentStatement);
5802
5803#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)]
5804pub struct ColumnName<T: AstInfo> {
5805    pub relation: T::ItemName,
5806    pub column: T::ColumnReference,
5807}
5808
5809impl<T: AstInfo> AstDisplay for ColumnName<T> {
5810    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5811        f.write_node(&self.relation);
5812        f.write_str(".");
5813        f.write_node(&self.column);
5814    }
5815}
5816impl_display_t!(ColumnName);
5817
5818#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5819pub enum CommentObjectType<T: AstInfo> {
5820    Table { name: T::ItemName },
5821    View { name: T::ItemName },
5822    Column { name: ColumnName<T> },
5823    MaterializedView { name: T::ItemName },
5824    Source { name: T::ItemName },
5825    Sink { name: T::ItemName },
5826    Index { name: T::ItemName },
5827    Func { name: T::ItemName },
5828    Connection { name: T::ItemName },
5829    Type { ty: T::DataType },
5830    Secret { name: T::ItemName },
5831    Role { name: T::RoleName },
5832    Database { name: T::DatabaseName },
5833    Schema { name: T::SchemaName },
5834    Cluster { name: T::ClusterName },
5835    ClusterReplica { name: QualifiedReplica },
5836    NetworkPolicy { name: T::NetworkPolicyName },
5837}
5838
5839impl<T: AstInfo> AstDisplay for CommentObjectType<T> {
5840    fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
5841        use CommentObjectType::*;
5842
5843        match self {
5844            Table { name } => {
5845                f.write_str("TABLE ");
5846                f.write_node(name);
5847            }
5848            View { name } => {
5849                f.write_str("VIEW ");
5850                f.write_node(name);
5851            }
5852            Column { name } => {
5853                f.write_str("COLUMN ");
5854                f.write_node(name);
5855            }
5856            MaterializedView { name } => {
5857                f.write_str("MATERIALIZED VIEW ");
5858                f.write_node(name);
5859            }
5860            Source { name } => {
5861                f.write_str("SOURCE ");
5862                f.write_node(name);
5863            }
5864            Sink { name } => {
5865                f.write_str("SINK ");
5866                f.write_node(name);
5867            }
5868            Index { name } => {
5869                f.write_str("INDEX ");
5870                f.write_node(name);
5871            }
5872            Func { name } => {
5873                f.write_str("FUNCTION ");
5874                f.write_node(name);
5875            }
5876            Connection { name } => {
5877                f.write_str("CONNECTION ");
5878                f.write_node(name);
5879            }
5880            Type { ty } => {
5881                f.write_str("TYPE ");
5882                f.write_node(ty);
5883            }
5884            Secret { name } => {
5885                f.write_str("SECRET ");
5886                f.write_node(name);
5887            }
5888            Role { name } => {
5889                f.write_str("ROLE ");
5890                f.write_node(name);
5891            }
5892            Database { name } => {
5893                f.write_str("DATABASE ");
5894                f.write_node(name);
5895            }
5896            Schema { name } => {
5897                f.write_str("SCHEMA ");
5898                f.write_node(name);
5899            }
5900            Cluster { name } => {
5901                f.write_str("CLUSTER ");
5902                f.write_node(name);
5903            }
5904            ClusterReplica { name } => {
5905                f.write_str("CLUSTER REPLICA ");
5906                f.write_node(name);
5907            }
5908            NetworkPolicy { name } => {
5909                f.write_str("NETWORK POLICY ");
5910                f.write_node(name);
5911            }
5912        }
5913    }
5914}
5915
5916impl_display_t!(CommentObjectType);
5917
5918// Include the `AstDisplay` implementations for simple options derived by the
5919// crate's build.rs script.
5920include!(concat!(env!("OUT_DIR"), "/display.simple_options.rs"));