Skip to main content

mz_sql/
plan.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! SQL planning.
11//!
12//! SQL planning is the process of taking the abstract syntax tree of a
13//! [`Statement`] and turning it into a [`Plan`] that the dataflow layer can
14//! execute.
15//!
16//! Statements must be purified before they can be planned. See the
17//! [`pure`](crate::pure) module for details.
18
19// Internal module layout.
20//
21// The entry point for planning is `statement::handle_statement`. That function
22// dispatches to a more specific `handle` function for the particular statement
23// type. For most statements, this `handle` function is uninteresting and short,
24// but anything involving a `SELECT` statement gets complicated. `SELECT`
25// queries wind through the functions in the `query` module, starting with
26// `plan_root_query` and fanning out based on the contents of the `SELECT`
27// statement.
28
29use std::collections::{BTreeMap, BTreeSet};
30use std::num::NonZeroUsize;
31use std::str::FromStr;
32use std::time::Duration;
33
34use chrono::{DateTime, Utc};
35use enum_kinds::EnumKind;
36use ipnet::IpNet;
37use maplit::btreeset;
38use mz_adapter_types::compaction::CompactionWindow;
39use mz_controller_types::{ClusterId, ReplicaId};
40use mz_expr::{CollectionPlan, ColumnOrder, MapFilterProject, MirScalarExpr, RowSetFinishing};
41use mz_ore::now::{self, NOW_ZERO};
42use mz_pgcopy::CopyFormatParams;
43use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem};
44use mz_repr::explain::{ExplainConfig, ExplainFormat};
45use mz_repr::network_policy_id::NetworkPolicyId;
46use mz_repr::optimize::OptimizerFeatureOverrides;
47use mz_repr::refresh_schedule::RefreshSchedule;
48use mz_repr::role_id::RoleId;
49use mz_repr::{
50    CatalogItemId, ColumnIndex, ColumnName, Diff, GlobalId, RelationDesc, ReprColumnType, Row,
51    SqlColumnType, SqlRelationType, SqlScalarType, Timestamp, VersionedRelationDesc,
52};
53use mz_sql_parser::ast::{
54    AlterSourceAddSubsourceOption, ClusterAlterOptionValue, ConnectionOptionName, CreateSinkOption,
55    CreateSinkOptionName, QualifiedReplica, RawDataType, SelectStatement,
56    TransactionIsolationLevel, TransactionMode, UnresolvedItemName, Value, WithOptionValue,
57};
58use mz_ssh_util::keys::SshKeyPair;
59use mz_storage_types::connections::aws::AwsConnection;
60use mz_storage_types::connections::gcp::{GcpConnection, GcpServiceAccountKeyTokenUri};
61use mz_storage_types::connections::inline::ReferencedConnection;
62use mz_storage_types::connections::{
63    AwsPrivatelinkConnection, CsrConnection, GlueSchemaRegistryConnection,
64    IcebergCatalogConnection, KafkaConnection, MySqlConnection, PostgresConnection,
65    SqlServerConnectionDetails, SshConnection,
66};
67use mz_storage_types::instances::StorageInstanceId;
68use mz_storage_types::sinks::{S3SinkFormat, SinkEnvelope, StorageSinkConnection};
69use mz_storage_types::sources::{
70    SourceDesc, SourceExportDataConfig, SourceExportDetails, Timeline,
71};
72use proptest_derive::Arbitrary;
73use serde::{Deserialize, Serialize};
74
75use crate::ast::{
76    ExplainStage, Expr, FetchDirection, NoticeSeverity, Raw, Statement, StatementKind,
77    TransactionAccessMode,
78};
79use crate::catalog::{
80    CatalogType, DefaultPrivilegeAclItem, DefaultPrivilegeObject, IdReference, ObjectType,
81    RoleAttributesRaw,
82};
83use crate::names::{
84    Aug, CommentObjectId, DependencyIds, FullItemName, ObjectId, QualifiedItemName,
85    ResolvedDatabaseSpecifier, ResolvedIds, SchemaSpecifier, SystemObjectId,
86};
87
88pub(crate) mod error;
89pub(crate) mod explain;
90pub(crate) mod hir;
91pub(crate) mod literal;
92pub(crate) mod lowering;
93pub(crate) mod notice;
94pub(crate) mod plan_utils;
95pub(crate) mod query;
96pub(crate) mod scope;
97pub(crate) mod side_effecting_func;
98pub(crate) mod statement;
99pub(crate) mod transform_ast;
100pub(crate) mod transform_hir;
101pub(crate) mod typeconv;
102pub(crate) mod with_options;
103
104use crate::plan;
105use crate::plan::statement::ddl::ClusterAlterUntilReadyOptionExtracted;
106use crate::plan::with_options::OptionalDuration;
107pub use error::PlanError;
108pub use explain::normalize_subqueries;
109pub use hir::{
110    AggregateExpr, CoercibleScalarExpr, Hir, HirRelationExpr, HirScalarExpr, JoinKind,
111    WindowExprType,
112};
113pub use lowering::Config as HirToMirConfig;
114pub use notice::PlanNotice;
115pub use query::{ExprContext, QueryContext, QueryLifetime};
116pub use scope::Scope;
117pub use side_effecting_func::SideEffectingFunc;
118pub use statement::ddl::{
119    AlterSourceAddSubsourceOptionExtracted, MySqlConfigOptionExtracted, PgConfigOptionExtracted,
120    PlannedAlterRoleOption, PlannedRoleAttributes, PlannedRoleVariable,
121    SqlServerConfigOptionExtracted,
122};
123pub use statement::{
124    StatementClassification, StatementContext, StatementDesc, describe, plan, plan_copy_from,
125    resolve_cluster_for_materialized_view,
126};
127pub use with_options::TryFromValue;
128
129use self::statement::ddl::ClusterAlterOptionExtracted;
130
131/// Instructions for executing a SQL query.
132#[derive(Debug, EnumKind)]
133#[enum_kind(PlanKind)]
134pub enum Plan {
135    CreateConnection(CreateConnectionPlan),
136    CreateDatabase(CreateDatabasePlan),
137    CreateSchema(CreateSchemaPlan),
138    CreateRole(CreateRolePlan),
139    CreateCluster(CreateClusterPlan),
140    CreateClusterReplica(CreateClusterReplicaPlan),
141    CreateSource(CreateSourcePlan),
142    CreateSources(Vec<CreateSourcePlanBundle>),
143    CreateSecret(CreateSecretPlan),
144    CreateSink(CreateSinkPlan),
145    CreateTable(CreateTablePlan),
146    CreateView(CreateViewPlan),
147    CreateMaterializedView(CreateMaterializedViewPlan),
148    CreateNetworkPolicy(CreateNetworkPolicyPlan),
149    CreateIndex(CreateIndexPlan),
150    CreateType(CreateTypePlan),
151    Comment(CommentPlan),
152    DiscardTemp,
153    DiscardAll,
154    DropObjects(DropObjectsPlan),
155    DropOwned(DropOwnedPlan),
156    EmptyQuery,
157    ShowAllVariables,
158    ShowCreate(ShowCreatePlan),
159    ShowColumns(ShowColumnsPlan),
160    ShowVariable(ShowVariablePlan),
161    InspectShard(InspectShardPlan),
162    SetVariable(SetVariablePlan),
163    ResetVariable(ResetVariablePlan),
164    SetTransaction(SetTransactionPlan),
165    StartTransaction(StartTransactionPlan),
166    CommitTransaction(CommitTransactionPlan),
167    AbortTransaction(AbortTransactionPlan),
168    Select(SelectPlan),
169    Subscribe(SubscribePlan),
170    CopyFrom(CopyFromPlan),
171    CopyTo(CopyToPlan),
172    ExplainPlan(ExplainPlanPlan),
173    ExplainPushdown(ExplainPushdownPlan),
174    ExplainTimestamp(ExplainTimestampPlan),
175    ExplainSinkSchema(ExplainSinkSchemaPlan),
176    Insert(InsertPlan),
177    AlterCluster(AlterClusterPlan),
178    AlterClusterSwap(AlterClusterSwapPlan),
179    AlterNoop(AlterNoopPlan),
180    AlterSetCluster(AlterSetClusterPlan),
181    AlterConnection(AlterConnectionPlan),
182    AlterSource(AlterSourcePlan),
183    AlterClusterRename(AlterClusterRenamePlan),
184    AlterClusterReplicaRename(AlterClusterReplicaRenamePlan),
185    AlterItemRename(AlterItemRenamePlan),
186    AlterSchemaRename(AlterSchemaRenamePlan),
187    AlterSchemaSwap(AlterSchemaSwapPlan),
188    AlterSecret(AlterSecretPlan),
189    AlterSink(AlterSinkPlan),
190    AlterSystemSet(AlterSystemSetPlan),
191    AlterSystemReset(AlterSystemResetPlan),
192    AlterSystemResetAll(AlterSystemResetAllPlan),
193    AlterRole(AlterRolePlan),
194    AlterOwner(AlterOwnerPlan),
195    AlterTableAddColumn(AlterTablePlan),
196    AlterMaterializedViewApplyReplacement(AlterMaterializedViewApplyReplacementPlan),
197    AlterNetworkPolicy(AlterNetworkPolicyPlan),
198    Declare(DeclarePlan),
199    Fetch(FetchPlan),
200    Close(ClosePlan),
201    ReadThenWrite(ReadThenWritePlan),
202    Prepare(PreparePlan),
203    Execute(ExecutePlan),
204    Deallocate(DeallocatePlan),
205    Raise(RaisePlan),
206    GrantRole(GrantRolePlan),
207    RevokeRole(RevokeRolePlan),
208    GrantPrivileges(GrantPrivilegesPlan),
209    RevokePrivileges(RevokePrivilegesPlan),
210    AlterDefaultPrivileges(AlterDefaultPrivilegesPlan),
211    ReassignOwned(ReassignOwnedPlan),
212    SideEffectingFunc(SideEffectingFunc),
213    ValidateConnection(ValidateConnectionPlan),
214    AlterRetainHistory(AlterRetainHistoryPlan),
215    AlterSourceTimestampInterval(AlterSourceTimestampIntervalPlan),
216}
217
218impl Plan {
219    /// Expresses which [`StatementKind`] can generate which set of
220    /// [`PlanKind`].
221    pub fn generated_from(stmt: &StatementKind) -> &'static [PlanKind] {
222        match stmt {
223            StatementKind::AlterCluster => &[PlanKind::AlterNoop, PlanKind::AlterCluster],
224            StatementKind::AlterConnection => &[PlanKind::AlterNoop, PlanKind::AlterConnection],
225            StatementKind::AlterDefaultPrivileges => &[PlanKind::AlterDefaultPrivileges],
226            StatementKind::AlterIndex => &[PlanKind::AlterRetainHistory, PlanKind::AlterNoop],
227            StatementKind::AlterObjectRename => &[
228                PlanKind::AlterClusterRename,
229                PlanKind::AlterClusterReplicaRename,
230                PlanKind::AlterItemRename,
231                PlanKind::AlterSchemaRename,
232                PlanKind::AlterNoop,
233            ],
234            StatementKind::AlterObjectSwap => &[
235                PlanKind::AlterClusterSwap,
236                PlanKind::AlterSchemaSwap,
237                PlanKind::AlterNoop,
238            ],
239            StatementKind::AlterRole => &[PlanKind::AlterRole],
240            StatementKind::AlterNetworkPolicy => &[PlanKind::AlterNetworkPolicy],
241            StatementKind::AlterSecret => &[PlanKind::AlterNoop, PlanKind::AlterSecret],
242            StatementKind::AlterSetCluster => &[PlanKind::AlterNoop, PlanKind::AlterSetCluster],
243            StatementKind::AlterSink => &[PlanKind::AlterNoop, PlanKind::AlterSink],
244            StatementKind::AlterSource => &[
245                PlanKind::AlterNoop,
246                PlanKind::AlterSource,
247                PlanKind::AlterRetainHistory,
248                PlanKind::AlterSourceTimestampInterval,
249            ],
250            StatementKind::AlterSystemReset => &[PlanKind::AlterNoop, PlanKind::AlterSystemReset],
251            StatementKind::AlterSystemResetAll => {
252                &[PlanKind::AlterNoop, PlanKind::AlterSystemResetAll]
253            }
254            StatementKind::AlterSystemSet => &[PlanKind::AlterNoop, PlanKind::AlterSystemSet],
255            StatementKind::AlterOwner => &[PlanKind::AlterNoop, PlanKind::AlterOwner],
256            StatementKind::AlterTableAddColumn => {
257                &[PlanKind::AlterNoop, PlanKind::AlterTableAddColumn]
258            }
259            StatementKind::AlterMaterializedViewApplyReplacement => &[
260                PlanKind::AlterNoop,
261                PlanKind::AlterMaterializedViewApplyReplacement,
262            ],
263            StatementKind::Close => &[PlanKind::Close],
264            StatementKind::Comment => &[PlanKind::Comment],
265            StatementKind::Commit => &[PlanKind::CommitTransaction],
266            StatementKind::Copy => &[
267                PlanKind::CopyFrom,
268                PlanKind::Select,
269                PlanKind::Subscribe,
270                PlanKind::CopyTo,
271            ],
272            StatementKind::CreateCluster => &[PlanKind::CreateCluster],
273            StatementKind::CreateClusterReplica => &[PlanKind::CreateClusterReplica],
274            StatementKind::CreateConnection => &[PlanKind::CreateConnection],
275            StatementKind::CreateDatabase => &[PlanKind::CreateDatabase],
276            StatementKind::CreateIndex => &[PlanKind::CreateIndex],
277            StatementKind::CreateNetworkPolicy => &[PlanKind::CreateNetworkPolicy],
278            StatementKind::CreateMaterializedView => &[PlanKind::CreateMaterializedView],
279            StatementKind::CreateRole => &[PlanKind::CreateRole],
280            StatementKind::CreateSchema => &[PlanKind::CreateSchema],
281            StatementKind::CreateSecret => &[PlanKind::CreateSecret],
282            StatementKind::CreateSink => &[PlanKind::CreateSink],
283            StatementKind::CreateSource | StatementKind::CreateSubsource => {
284                &[PlanKind::CreateSource]
285            }
286            StatementKind::CreateWebhookSource => &[PlanKind::CreateSource, PlanKind::CreateTable],
287            StatementKind::CreateTable => &[PlanKind::CreateTable],
288            StatementKind::CreateTableFromSource => &[PlanKind::CreateTable],
289            StatementKind::CreateType => &[PlanKind::CreateType],
290            StatementKind::CreateView => &[PlanKind::CreateView],
291            StatementKind::Deallocate => &[PlanKind::Deallocate],
292            StatementKind::Declare => &[PlanKind::Declare],
293            StatementKind::Delete => &[PlanKind::ReadThenWrite],
294            StatementKind::Discard => &[PlanKind::DiscardAll, PlanKind::DiscardTemp],
295            StatementKind::DropObjects => &[PlanKind::DropObjects],
296            StatementKind::DropOwned => &[PlanKind::DropOwned],
297            StatementKind::Execute => &[PlanKind::Execute],
298            StatementKind::ExplainPlan => &[PlanKind::ExplainPlan],
299            StatementKind::ExplainPushdown => &[PlanKind::ExplainPushdown],
300            StatementKind::ExplainAnalyzeObject => &[PlanKind::Select],
301            StatementKind::ExplainAnalyzeCluster => &[PlanKind::Select],
302            StatementKind::ExplainTimestamp => &[PlanKind::ExplainTimestamp],
303            StatementKind::ExplainSinkSchema => &[PlanKind::ExplainSinkSchema],
304            StatementKind::Fetch => &[PlanKind::Fetch],
305            StatementKind::GrantPrivileges => &[PlanKind::GrantPrivileges],
306            StatementKind::GrantRole => &[PlanKind::GrantRole],
307            StatementKind::Insert => &[PlanKind::Insert],
308            StatementKind::Prepare => &[PlanKind::Prepare],
309            StatementKind::Raise => &[PlanKind::Raise],
310            StatementKind::ReassignOwned => &[PlanKind::ReassignOwned],
311            StatementKind::ResetVariable => &[PlanKind::ResetVariable],
312            StatementKind::RevokePrivileges => &[PlanKind::RevokePrivileges],
313            StatementKind::RevokeRole => &[PlanKind::RevokeRole],
314            StatementKind::Rollback => &[PlanKind::AbortTransaction],
315            StatementKind::Select => &[PlanKind::Select, PlanKind::SideEffectingFunc],
316            StatementKind::SetTransaction => &[PlanKind::SetTransaction],
317            StatementKind::SetVariable => &[PlanKind::SetVariable],
318            StatementKind::Show => &[
319                PlanKind::Select,
320                PlanKind::ShowVariable,
321                PlanKind::ShowCreate,
322                PlanKind::ShowColumns,
323                PlanKind::ShowAllVariables,
324                PlanKind::InspectShard,
325            ],
326            StatementKind::StartTransaction => &[PlanKind::StartTransaction],
327            StatementKind::Subscribe => &[PlanKind::Subscribe],
328            StatementKind::Update => &[PlanKind::ReadThenWrite],
329            StatementKind::ValidateConnection => &[PlanKind::ValidateConnection],
330            StatementKind::AlterRetainHistory => &[PlanKind::AlterRetainHistory],
331            StatementKind::ExecuteUnitTest => &[],
332        }
333    }
334
335    /// Returns a human readable name of the plan. Meant for use in messages sent back to a user.
336    pub fn name(&self) -> &str {
337        match self {
338            Plan::CreateConnection(_) => "create connection",
339            Plan::CreateDatabase(_) => "create database",
340            Plan::CreateSchema(_) => "create schema",
341            Plan::CreateRole(_) => "create role",
342            Plan::CreateCluster(_) => "create cluster",
343            Plan::CreateClusterReplica(_) => "create cluster replica",
344            Plan::CreateSource(_) => "create source",
345            Plan::CreateSources(_) => "create source",
346            Plan::CreateSecret(_) => "create secret",
347            Plan::CreateSink(_) => "create sink",
348            Plan::CreateTable(_) => "create table",
349            Plan::CreateView(_) => "create view",
350            Plan::CreateMaterializedView(_) => "create materialized view",
351            Plan::CreateIndex(_) => "create index",
352            Plan::CreateType(_) => "create type",
353            Plan::CreateNetworkPolicy(_) => "create network policy",
354            Plan::Comment(_) => "comment",
355            Plan::DiscardTemp => "discard temp",
356            Plan::DiscardAll => "discard all",
357            Plan::DropObjects(plan) => match plan.object_type {
358                ObjectType::Table => "drop table",
359                ObjectType::View => "drop view",
360                ObjectType::MaterializedView => "drop materialized view",
361                ObjectType::Source => "drop source",
362                ObjectType::Sink => "drop sink",
363                ObjectType::Index => "drop index",
364                ObjectType::Type => "drop type",
365                ObjectType::Role => "drop roles",
366                ObjectType::Cluster => "drop clusters",
367                ObjectType::ClusterReplica => "drop cluster replicas",
368                ObjectType::Secret => "drop secret",
369                ObjectType::Connection => "drop connection",
370                ObjectType::Database => "drop database",
371                ObjectType::Schema => "drop schema",
372                ObjectType::Func => "drop function",
373                ObjectType::NetworkPolicy => "drop network policy",
374            },
375            Plan::DropOwned(_) => "drop owned",
376            Plan::EmptyQuery => "do nothing",
377            Plan::ShowAllVariables => "show all variables",
378            Plan::ShowCreate(_) => "show create",
379            Plan::ShowColumns(_) => "show columns",
380            Plan::ShowVariable(_) => "show variable",
381            Plan::InspectShard(_) => "inspect shard",
382            Plan::SetVariable(_) => "set variable",
383            Plan::ResetVariable(_) => "reset variable",
384            Plan::SetTransaction(_) => "set transaction",
385            Plan::StartTransaction(_) => "start transaction",
386            Plan::CommitTransaction(_) => "commit",
387            Plan::AbortTransaction(_) => "abort",
388            Plan::Select(_) => "select",
389            Plan::Subscribe(_) => "subscribe",
390            Plan::CopyFrom(_) => "copy from",
391            Plan::CopyTo(_) => "copy to",
392            Plan::ExplainPlan(_) => "explain plan",
393            Plan::ExplainPushdown(_) => "EXPLAIN FILTER PUSHDOWN",
394            Plan::ExplainTimestamp(_) => "explain timestamp",
395            Plan::ExplainSinkSchema(_) => "explain schema",
396            Plan::Insert(_) => "insert",
397            Plan::AlterNoop(plan) => match plan.object_type {
398                ObjectType::Table => "alter table",
399                ObjectType::View => "alter view",
400                ObjectType::MaterializedView => "alter materialized view",
401                ObjectType::Source => "alter source",
402                ObjectType::Sink => "alter sink",
403                ObjectType::Index => "alter index",
404                ObjectType::Type => "alter type",
405                ObjectType::Role => "alter role",
406                ObjectType::Cluster => "alter cluster",
407                ObjectType::ClusterReplica => "alter cluster replica",
408                ObjectType::Secret => "alter secret",
409                ObjectType::Connection => "alter connection",
410                ObjectType::Database => "alter database",
411                ObjectType::Schema => "alter schema",
412                ObjectType::Func => "alter function",
413                ObjectType::NetworkPolicy => "alter network policy",
414            },
415            Plan::AlterCluster(_) => "alter cluster",
416            Plan::AlterClusterRename(_) => "alter cluster rename",
417            Plan::AlterClusterSwap(_) => "alter cluster swap",
418            Plan::AlterClusterReplicaRename(_) => "alter cluster replica rename",
419            Plan::AlterSetCluster(_) => "alter set cluster",
420            Plan::AlterConnection(_) => "alter connection",
421            Plan::AlterSource(_) => "alter source",
422            Plan::AlterItemRename(_) => "rename item",
423            Plan::AlterSchemaRename(_) => "alter rename schema",
424            Plan::AlterSchemaSwap(_) => "alter swap schema",
425            Plan::AlterSecret(_) => "alter secret",
426            Plan::AlterSink(_) => "alter sink",
427            Plan::AlterSystemSet(_) => "alter system",
428            Plan::AlterSystemReset(_) => "alter system",
429            Plan::AlterSystemResetAll(_) => "alter system",
430            Plan::AlterRole(_) => "alter role",
431            Plan::AlterNetworkPolicy(_) => "alter network policy",
432            Plan::AlterOwner(plan) => match plan.object_type {
433                ObjectType::Table => "alter table owner",
434                ObjectType::View => "alter view owner",
435                ObjectType::MaterializedView => "alter materialized view owner",
436                ObjectType::Source => "alter source owner",
437                ObjectType::Sink => "alter sink owner",
438                ObjectType::Index => "alter index owner",
439                ObjectType::Type => "alter type owner",
440                ObjectType::Role => "alter role owner",
441                ObjectType::Cluster => "alter cluster owner",
442                ObjectType::ClusterReplica => "alter cluster replica owner",
443                ObjectType::Secret => "alter secret owner",
444                ObjectType::Connection => "alter connection owner",
445                ObjectType::Database => "alter database owner",
446                ObjectType::Schema => "alter schema owner",
447                ObjectType::Func => "alter function owner",
448                ObjectType::NetworkPolicy => "alter network policy owner",
449            },
450            Plan::AlterTableAddColumn(_) => "alter table add column",
451            Plan::AlterMaterializedViewApplyReplacement(_) => {
452                "alter materialized view apply replacement"
453            }
454            Plan::Declare(_) => "declare",
455            Plan::Fetch(_) => "fetch",
456            Plan::Close(_) => "close",
457            Plan::ReadThenWrite(plan) => match plan.kind {
458                MutationKind::Insert => "insert into select",
459                MutationKind::Update => "update",
460                MutationKind::Delete => "delete",
461            },
462            Plan::Prepare(_) => "prepare",
463            Plan::Execute(_) => "execute",
464            Plan::Deallocate(_) => "deallocate",
465            Plan::Raise(_) => "raise",
466            Plan::GrantRole(_) => "grant role",
467            Plan::RevokeRole(_) => "revoke role",
468            Plan::GrantPrivileges(_) => "grant privilege",
469            Plan::RevokePrivileges(_) => "revoke privilege",
470            Plan::AlterDefaultPrivileges(_) => "alter default privileges",
471            Plan::ReassignOwned(_) => "reassign owned",
472            Plan::SideEffectingFunc(_) => "side effecting func",
473            Plan::ValidateConnection(_) => "validate connection",
474            Plan::AlterRetainHistory(_) => "alter retain history",
475            Plan::AlterSourceTimestampInterval(_) => "alter source timestamp interval",
476        }
477    }
478
479    /// Returns `true` iff this `Plan` is allowed to be executed in read-only
480    /// mode.
481    ///
482    /// We use an explicit allow-list, to avoid future additions automatically
483    /// falling into the `true` category.
484    pub fn allowed_in_read_only(&self) -> bool {
485        match self {
486            // These two set non-durable session variables, so are okay in
487            // read-only mode.
488            Plan::SetVariable(_) => true,
489            Plan::ResetVariable(_) => true,
490            Plan::SetTransaction(_) => true,
491            Plan::StartTransaction(_) => true,
492            Plan::CommitTransaction(_) => true,
493            Plan::AbortTransaction(_) => true,
494            Plan::Select(_) => true,
495            Plan::EmptyQuery => true,
496            Plan::ShowAllVariables => true,
497            Plan::ShowCreate(_) => true,
498            Plan::ShowColumns(_) => true,
499            Plan::ShowVariable(_) => true,
500            Plan::InspectShard(_) => true,
501            Plan::Subscribe(_) => true,
502            Plan::CopyTo(_) => true,
503            Plan::ExplainPlan(_) => true,
504            Plan::ExplainPushdown(_) => true,
505            Plan::ExplainTimestamp(_) => true,
506            Plan::ExplainSinkSchema(_) => true,
507            Plan::ValidateConnection(_) => true,
508            _ => false,
509        }
510    }
511}
512
513#[derive(Debug)]
514pub struct StartTransactionPlan {
515    pub access: Option<TransactionAccessMode>,
516    pub isolation_level: Option<TransactionIsolationLevel>,
517}
518
519#[derive(Debug)]
520pub enum TransactionType {
521    Explicit,
522    Implicit,
523}
524
525impl TransactionType {
526    pub fn is_explicit(&self) -> bool {
527        matches!(self, TransactionType::Explicit)
528    }
529
530    pub fn is_implicit(&self) -> bool {
531        matches!(self, TransactionType::Implicit)
532    }
533}
534
535#[derive(Debug)]
536pub struct CommitTransactionPlan {
537    pub transaction_type: TransactionType,
538}
539
540#[derive(Debug)]
541pub struct AbortTransactionPlan {
542    pub transaction_type: TransactionType,
543}
544
545#[derive(Debug)]
546pub struct CreateDatabasePlan {
547    pub name: String,
548    pub if_not_exists: bool,
549}
550
551#[derive(Debug)]
552pub struct CreateSchemaPlan {
553    pub database_spec: ResolvedDatabaseSpecifier,
554    pub schema_name: String,
555    pub if_not_exists: bool,
556}
557
558#[derive(Debug)]
559pub struct CreateRolePlan {
560    pub name: String,
561    pub attributes: RoleAttributesRaw,
562}
563
564#[derive(Debug, PartialEq, Eq, Clone)]
565pub struct CreateClusterPlan {
566    pub name: String,
567    pub variant: CreateClusterVariant,
568    pub workload_class: Option<String>,
569}
570
571#[derive(Debug, PartialEq, Eq, Clone)]
572pub enum CreateClusterVariant {
573    Managed(CreateClusterManagedPlan),
574    Unmanaged(CreateClusterUnmanagedPlan),
575}
576
577#[derive(Debug, PartialEq, Eq, Clone)]
578pub struct CreateClusterUnmanagedPlan {
579    pub replicas: Vec<(String, ReplicaConfig)>,
580}
581
582#[derive(Debug, PartialEq, Eq, Clone)]
583pub struct CreateClusterManagedPlan {
584    pub replication_factor: u32,
585    pub size: String,
586    pub availability_zones: Vec<String>,
587    pub compute: ComputeReplicaConfig,
588    pub optimizer_feature_overrides: OptimizerFeatureOverrides,
589    pub schedule: ClusterSchedule,
590    /// The user-configured autoscaling policy, or `None` if autoscaling is
591    /// disabled for the cluster.
592    pub auto_scaling_strategy: Option<AutoScalingStrategy>,
593}
594
595#[derive(Debug)]
596pub struct CreateClusterReplicaPlan {
597    pub cluster_id: ClusterId,
598    pub name: String,
599    pub config: ReplicaConfig,
600}
601
602/// Configuration of introspection for a cluster replica.
603#[derive(
604    Clone,
605    Copy,
606    Debug,
607    Serialize,
608    Deserialize,
609    PartialOrd,
610    Ord,
611    PartialEq,
612    Eq
613)]
614pub struct ComputeReplicaIntrospectionConfig {
615    /// Whether to introspect the introspection.
616    pub debugging: bool,
617    /// The interval at which to introspect.
618    pub interval: Duration,
619}
620
621#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
622pub struct ComputeReplicaConfig {
623    pub introspection: Option<ComputeReplicaIntrospectionConfig>,
624    /// Whether arrangements on this replica request dictionary compression. The
625    /// gating feature flag decides whether a replica honors this value at
626    /// creation time.
627    pub arrangement_compression: bool,
628}
629
630#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
631pub enum ReplicaConfig {
632    Unorchestrated {
633        storagectl_addrs: Vec<String>,
634        computectl_addrs: Vec<String>,
635        compute: ComputeReplicaConfig,
636    },
637    Orchestrated {
638        size: String,
639        availability_zone: Option<String>,
640        compute: ComputeReplicaConfig,
641        internal: bool,
642        billed_as: Option<String>,
643    },
644}
645
646#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
647pub enum ClusterSchedule {
648    /// The system won't automatically turn the cluster On or Off.
649    Manual,
650    /// The cluster will be On when a REFRESH materialized view on it needs to refresh.
651    /// `hydration_time_estimate` determines how much time before a refresh to turn the
652    /// cluster On, so that it can rehydrate already before the refresh time.
653    Refresh { hydration_time_estimate: Duration },
654}
655
656impl Default for ClusterSchedule {
657    fn default() -> Self {
658        // (Has to be consistent with `impl Default for ClusterScheduleOptionValue`.)
659        ClusterSchedule::Manual
660    }
661}
662
663/// The user-configured autoscaling policy of a managed cluster.
664///
665/// Extensible: future strategies are added as additional optional sub-policies,
666/// so the block as a whole can grow without changing existing ones.
667#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
668pub struct AutoScalingStrategy {
669    pub on_hydration: Option<OnHydration>,
670}
671
672/// The `ON HYDRATION` autoscaling sub-policy: while objects are un-hydrated, run
673/// an extra replica at `hydration_size` to accelerate hydration.
674#[derive(Clone, Debug, Deserialize, Serialize, PartialOrd, PartialEq, Eq, Ord)]
675pub struct OnHydration {
676    pub hydration_size: String,
677    /// How long the burst replica lingers after the steady-state replicas
678    /// hydrate. `None` falls back to the system default at the controller.
679    pub linger_duration: Option<Duration>,
680}
681
682#[derive(Debug)]
683pub struct CreateSourcePlan {
684    pub name: QualifiedItemName,
685    pub source: Source,
686    pub if_not_exists: bool,
687    pub timeline: Timeline,
688    // None for subsources, which run on the parent cluster.
689    pub in_cluster: Option<ClusterId>,
690}
691
692#[derive(Clone, Debug, PartialEq, Eq)]
693pub struct SourceReferences {
694    pub updated_at: u64,
695    pub references: Vec<SourceReference>,
696}
697
698/// An available external reference for a source and if possible to retrieve,
699/// any column names it contains.
700#[derive(Clone, Debug, PartialEq, Eq)]
701pub struct SourceReference {
702    pub name: String,
703    pub namespace: Option<String>,
704    pub columns: Vec<String>,
705}
706
707/// A [`CreateSourcePlan`] and the metadata necessary to sequence it.
708#[derive(Debug)]
709pub struct CreateSourcePlanBundle {
710    /// ID of this source in the Catalog.
711    pub item_id: CatalogItemId,
712    /// ID used to reference this source from outside the catalog, e.g. compute.
713    pub global_id: GlobalId,
714    /// Details of the source to create.
715    pub plan: CreateSourcePlan,
716    /// Other catalog objects that are referenced by this source, determined at name resolution.
717    pub resolved_ids: ResolvedIds,
718    /// All the available upstream references for this source.
719    /// Populated for top-level sources that can contain subsources/tables
720    /// and used during sequencing to populate the appropriate catalog fields.
721    pub available_source_references: Option<SourceReferences>,
722}
723
724#[derive(Debug)]
725pub struct CreateConnectionPlan {
726    pub name: QualifiedItemName,
727    pub if_not_exists: bool,
728    pub connection: Connection,
729    pub validate: bool,
730}
731
732#[derive(Debug)]
733pub struct ValidateConnectionPlan {
734    /// ID of the connection in the Catalog.
735    pub id: CatalogItemId,
736    /// The connection to validate.
737    pub connection: mz_storage_types::connections::Connection<ReferencedConnection>,
738}
739
740#[derive(Debug)]
741pub struct CreateSecretPlan {
742    pub name: QualifiedItemName,
743    pub secret: Secret,
744    pub if_not_exists: bool,
745}
746
747#[derive(Debug)]
748pub struct CreateSinkPlan {
749    pub name: QualifiedItemName,
750    pub sink: Sink,
751    pub with_snapshot: bool,
752    pub if_not_exists: bool,
753    pub in_cluster: ClusterId,
754}
755
756#[derive(Debug)]
757pub struct CreateTablePlan {
758    pub name: QualifiedItemName,
759    pub table: Table,
760    pub if_not_exists: bool,
761}
762
763#[derive(Debug, Clone)]
764pub struct CreateViewPlan {
765    pub name: QualifiedItemName,
766    pub view: View,
767    /// The Catalog objects that this view is replacing, if any.
768    pub replace: Option<CatalogItemId>,
769    /// The Catalog objects that need to be dropped. This includes `replace` and any dependents.
770    pub drop_ids: Vec<CatalogItemId>,
771    pub if_not_exists: bool,
772    /// True if the view contains an expression that can make the exact column list
773    /// ambiguous. For example `NATURAL JOIN` or `SELECT *`.
774    pub ambiguous_columns: bool,
775}
776
777#[derive(Debug, Clone)]
778pub struct CreateMaterializedViewPlan {
779    pub name: QualifiedItemName,
780    pub materialized_view: MaterializedView,
781    /// The Catalog objects that this materialized view is replacing, if any.
782    pub replace: Option<CatalogItemId>,
783    /// The Catalog objects that need to be dropped. This includes `replace` and any dependents.
784    pub drop_ids: Vec<CatalogItemId>,
785    pub if_not_exists: bool,
786    /// True if the materialized view contains an expression that can make the exact column list
787    /// ambiguous. For example `NATURAL JOIN` or `SELECT *`.
788    pub ambiguous_columns: bool,
789}
790
791#[derive(Debug, Clone)]
792pub struct CreateNetworkPolicyPlan {
793    pub name: String,
794    pub rules: Vec<NetworkPolicyRule>,
795}
796
797#[derive(Debug, Clone)]
798pub struct AlterNetworkPolicyPlan {
799    pub id: NetworkPolicyId,
800    pub name: String,
801    pub rules: Vec<NetworkPolicyRule>,
802}
803
804#[derive(Debug, Clone)]
805pub struct CreateIndexPlan {
806    pub name: QualifiedItemName,
807    pub index: Index,
808    pub if_not_exists: bool,
809}
810
811#[derive(Debug)]
812pub struct CreateTypePlan {
813    pub name: QualifiedItemName,
814    pub typ: Type,
815}
816
817#[derive(Debug)]
818pub struct DropObjectsPlan {
819    /// The IDs of only the objects directly referenced in the `DROP` statement.
820    pub referenced_ids: Vec<ObjectId>,
821    /// All object IDs to drop. Includes `referenced_ids` and all descendants.
822    pub drop_ids: Vec<ObjectId>,
823    /// The type of object that was dropped explicitly in the DROP statement. `ids` may contain
824    /// objects of different types due to CASCADE.
825    pub object_type: ObjectType,
826}
827
828#[derive(Debug)]
829pub struct DropOwnedPlan {
830    /// The role IDs that own the objects.
831    pub role_ids: Vec<RoleId>,
832    /// All object IDs to drop.
833    pub drop_ids: Vec<ObjectId>,
834    /// The privileges to revoke.
835    pub privilege_revokes: Vec<(SystemObjectId, MzAclItem)>,
836    /// The default privileges to revoke.
837    pub default_privilege_revokes: Vec<(DefaultPrivilegeObject, DefaultPrivilegeAclItem)>,
838}
839
840#[derive(Debug)]
841pub struct ShowVariablePlan {
842    pub name: String,
843}
844
845#[derive(Debug)]
846pub struct InspectShardPlan {
847    /// ID of the storage collection to inspect.
848    pub id: GlobalId,
849}
850
851#[derive(Debug)]
852pub struct SetVariablePlan {
853    pub name: String,
854    pub value: VariableValue,
855    pub local: bool,
856}
857
858#[derive(Debug)]
859pub enum VariableValue {
860    Default,
861    Values(Vec<String>),
862}
863
864#[derive(Debug)]
865pub struct ResetVariablePlan {
866    pub name: String,
867}
868
869#[derive(Debug)]
870pub struct SetTransactionPlan {
871    pub local: bool,
872    pub modes: Vec<TransactionMode>,
873}
874
875/// A plan for select statements.
876#[derive(Clone, Debug)]
877pub struct SelectPlan {
878    /// The `SELECT` statement itself. Used for explain/notices, but not otherwise
879    /// load-bearing. Boxed to save stack space.
880    pub select: Option<Box<SelectStatement<Aug>>>,
881    /// The plan as a HIR.
882    pub source: HirRelationExpr,
883    /// At what time should this select happen?
884    pub when: QueryWhen,
885    /// Instructions how to form the result set.
886    pub finishing: RowSetFinishing,
887    /// For `COPY TO STDOUT`, the format to use.
888    pub copy_to: Option<CopyFormat>,
889}
890
891impl SelectPlan {
892    pub fn immediate(rows: Vec<Row>, typ: SqlRelationType) -> Self {
893        let arity = typ.arity();
894        SelectPlan {
895            select: None,
896            source: HirRelationExpr::Constant { rows, typ },
897            when: QueryWhen::Immediately,
898            finishing: RowSetFinishing::trivial(arity),
899            copy_to: None,
900        }
901    }
902}
903
904#[derive(Debug, Clone)]
905pub enum SubscribeOutput {
906    Diffs,
907    WithinTimestampOrderBy {
908        /// We pretend that mz_diff is prepended to the normal columns, making it index 0
909        order_by: Vec<ColumnOrder>,
910    },
911    EnvelopeUpsert {
912        /// Order by with just keys
913        order_by_keys: Vec<ColumnOrder>,
914    },
915    EnvelopeDebezium {
916        /// Order by with just keys
917        order_by_keys: Vec<ColumnOrder>,
918    },
919}
920
921impl SubscribeOutput {
922    pub fn row_order(&self) -> &[ColumnOrder] {
923        match self {
924            SubscribeOutput::Diffs => &[],
925            // This ordering prepends the diff, so its `order_by` field cannot be applied to rows.
926            SubscribeOutput::WithinTimestampOrderBy { .. } => &[],
927            SubscribeOutput::EnvelopeUpsert { order_by_keys } => order_by_keys,
928            SubscribeOutput::EnvelopeDebezium { order_by_keys } => order_by_keys,
929        }
930    }
931}
932
933#[derive(Debug, Clone)]
934pub struct SubscribePlan {
935    pub from: SubscribeFrom,
936    pub with_snapshot: bool,
937    pub when: QueryWhen,
938    pub up_to: Option<Timestamp>,
939    pub copy_to: Option<CopyFormat>,
940    pub emit_progress: bool,
941    pub output: SubscribeOutput,
942}
943
944#[derive(Debug, Clone)]
945pub enum SubscribeFrom {
946    /// ID of the collection to subscribe to.
947    Id(GlobalId),
948    /// Query to subscribe to.
949    Query {
950        expr: HirRelationExpr,
951        desc: RelationDesc,
952    },
953}
954
955impl SubscribeFrom {
956    pub fn depends_on(&self) -> BTreeSet<GlobalId> {
957        match self {
958            SubscribeFrom::Id(id) => BTreeSet::from([*id]),
959            SubscribeFrom::Query { expr, .. } => expr.depends_on(),
960        }
961    }
962
963    pub fn contains_temporal(&self) -> bool {
964        match self {
965            SubscribeFrom::Id(_) => false,
966            SubscribeFrom::Query { expr, .. } => expr.contains_temporal(),
967        }
968    }
969}
970
971#[derive(Debug)]
972pub struct ShowCreatePlan {
973    pub id: ObjectId,
974    pub row: Row,
975}
976
977#[derive(Debug)]
978pub struct ShowColumnsPlan {
979    pub id: CatalogItemId,
980    pub select_plan: SelectPlan,
981    pub new_resolved_ids: ResolvedIds,
982}
983
984#[derive(Debug)]
985pub struct CopyFromPlan {
986    /// Table we're copying into.
987    pub target_id: CatalogItemId,
988    /// Human-readable full name of the target table.
989    pub target_name: String,
990    /// Source we're copying data from.
991    pub source: CopyFromSource,
992    /// How input columns map to those on the destination table.
993    ///
994    /// TODO(cf2): Remove this field in favor of the mfp.
995    pub columns: Vec<ColumnIndex>,
996    /// [`RelationDesc`] describing the input data.
997    pub source_desc: RelationDesc,
998    /// Changes the shape of the input data to match the destination table.
999    pub mfp: MapFilterProject,
1000    /// Format specific params for copying the input data.
1001    pub params: CopyFormatParams<'static>,
1002    /// Filter for the source files we're copying from, e.g. an S3 prefix.
1003    pub filter: Option<CopyFromFilter>,
1004}
1005
1006#[derive(Debug)]
1007pub enum CopyFromSource {
1008    /// Copying from a file local to the user, transmitted via pgwire.
1009    Stdin,
1010    /// A remote resource, e.g. HTTP file.
1011    ///
1012    /// The contained [`HirScalarExpr`] evaluates to the Url for the remote resource.
1013    Url(HirScalarExpr),
1014    /// A file in an S3 bucket.
1015    AwsS3 {
1016        /// Expression that evaluates to the file we want to copy.
1017        uri: HirScalarExpr,
1018        /// Details for how we connect to AWS S3.
1019        connection: AwsConnection,
1020        /// ID of the connection object.
1021        connection_id: CatalogItemId,
1022    },
1023}
1024
1025#[derive(Debug)]
1026pub enum CopyFromFilter {
1027    Files(Vec<String>),
1028    Pattern(String),
1029}
1030
1031/// `COPY TO S3`
1032///
1033/// (This is a completely different thing from `COPY TO STDOUT`. That is a `Plan::Select` with
1034/// `copy_to` set.)
1035#[derive(Debug, Clone)]
1036pub struct CopyToPlan {
1037    /// The select query plan whose data will be copied to destination uri.
1038    pub select_plan: SelectPlan,
1039    pub desc: RelationDesc,
1040    /// The scalar expression to be resolved to get the destination uri.
1041    pub to: HirScalarExpr,
1042    pub connection: mz_storage_types::connections::Connection<ReferencedConnection>,
1043    /// The ID of the connection.
1044    pub connection_id: CatalogItemId,
1045    pub format: S3SinkFormat,
1046    pub max_file_size: u64,
1047}
1048
1049#[derive(Clone, Debug)]
1050pub struct ExplainPlanPlan {
1051    pub stage: ExplainStage,
1052    pub format: ExplainFormat,
1053    pub config: ExplainConfig,
1054    pub explainee: Explainee,
1055}
1056
1057/// The type of object to be explained
1058#[derive(Clone, Debug)]
1059pub enum Explainee {
1060    /// Lookup and explain a plan saved for an view.
1061    View(CatalogItemId),
1062    /// Lookup and explain a plan saved for an existing materialized view.
1063    MaterializedView(CatalogItemId),
1064    /// Lookup and explain a plan saved for an existing index.
1065    Index(CatalogItemId),
1066    /// Replan an existing view.
1067    ReplanView(CatalogItemId),
1068    /// Replan an existing materialized view.
1069    ReplanMaterializedView(CatalogItemId),
1070    /// Replan an existing index.
1071    ReplanIndex(CatalogItemId),
1072    /// A SQL statement.
1073    Statement(ExplaineeStatement),
1074}
1075
1076/// Explainee types that are statements.
1077#[derive(Clone, Debug, EnumKind)]
1078#[enum_kind(ExplaineeStatementKind)]
1079pub enum ExplaineeStatement {
1080    /// The object to be explained is a SELECT statement.
1081    Select {
1082        /// Broken flag (see [`ExplaineeStatement::broken()`]).
1083        broken: bool,
1084        plan: plan::SelectPlan,
1085        desc: RelationDesc,
1086    },
1087    /// The object to be explained is a CREATE VIEW.
1088    CreateView {
1089        /// Broken flag (see [`ExplaineeStatement::broken()`]).
1090        broken: bool,
1091        plan: plan::CreateViewPlan,
1092    },
1093    /// The object to be explained is a CREATE MATERIALIZED VIEW.
1094    CreateMaterializedView {
1095        /// Broken flag (see [`ExplaineeStatement::broken()`]).
1096        broken: bool,
1097        plan: plan::CreateMaterializedViewPlan,
1098    },
1099    /// The object to be explained is a CREATE INDEX.
1100    CreateIndex {
1101        /// Broken flag (see [`ExplaineeStatement::broken()`]).
1102        broken: bool,
1103        plan: plan::CreateIndexPlan,
1104    },
1105    /// The object to be explained is a SUBSCRIBE statement.
1106    Subscribe {
1107        /// Broken flag (see [`ExplaineeStatement::broken()`]).
1108        broken: bool,
1109        plan: plan::SubscribePlan,
1110    },
1111}
1112
1113impl ExplaineeStatement {
1114    pub fn depends_on(&self) -> BTreeSet<GlobalId> {
1115        match self {
1116            Self::Select { plan, .. } => plan.source.depends_on(),
1117            Self::CreateView { plan, .. } => plan.view.expr.depends_on(),
1118            Self::CreateMaterializedView { plan, .. } => plan.materialized_view.expr.depends_on(),
1119            Self::CreateIndex { plan, .. } => btreeset! {plan.index.on},
1120            Self::Subscribe { plan, .. } => plan.from.depends_on(),
1121        }
1122    }
1123
1124    /// Statements that have their `broken` flag set are expected to cause a
1125    /// panic in the optimizer code. In this case:
1126    ///
1127    /// 1. The optimizer pipeline execution will stop, but the panic will be
1128    ///    intercepted and will not propagate to the caller. The partial
1129    ///    optimizer trace collected until this point will be available.
1130    /// 2. The optimizer trace tracing subscriber will delegate regular tracing
1131    ///    spans and events to the default subscriber.
1132    ///
1133    /// This is useful when debugging queries that cause panics.
1134    pub fn broken(&self) -> bool {
1135        match self {
1136            Self::Select { broken, .. } => *broken,
1137            Self::CreateView { broken, .. } => *broken,
1138            Self::CreateMaterializedView { broken, .. } => *broken,
1139            Self::CreateIndex { broken, .. } => *broken,
1140            Self::Subscribe { broken, .. } => *broken,
1141        }
1142    }
1143}
1144
1145impl ExplaineeStatementKind {
1146    pub fn supports(&self, stage: &ExplainStage) -> bool {
1147        use ExplainStage::*;
1148        match self {
1149            Self::Select => true,
1150            Self::CreateView => ![GlobalPlan, PhysicalPlan].contains(stage),
1151            Self::CreateMaterializedView => true,
1152            Self::CreateIndex => ![RawPlan, DecorrelatedPlan, LocalPlan].contains(stage),
1153            // SUBSCRIBE doesn't support RAW, DECORRELATED, or LOCAL stages because
1154            // it takes MIR directly rather than going through HIR lowering.
1155            Self::Subscribe => ![RawPlan, DecorrelatedPlan, LocalPlan].contains(stage),
1156        }
1157    }
1158}
1159
1160impl std::fmt::Display for ExplaineeStatementKind {
1161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1162        match self {
1163            Self::Select => write!(f, "SELECT"),
1164            Self::CreateView => write!(f, "CREATE VIEW"),
1165            Self::CreateMaterializedView => write!(f, "CREATE MATERIALIZED VIEW"),
1166            Self::CreateIndex => write!(f, "CREATE INDEX"),
1167            Self::Subscribe => write!(f, "SUBSCRIBE"),
1168        }
1169    }
1170}
1171
1172#[derive(Clone, Debug)]
1173pub struct ExplainPushdownPlan {
1174    pub explainee: Explainee,
1175}
1176
1177#[derive(Clone, Debug)]
1178pub struct ExplainTimestampPlan {
1179    pub format: ExplainFormat,
1180    pub raw_plan: HirRelationExpr,
1181    pub when: QueryWhen,
1182}
1183
1184#[derive(Debug)]
1185pub struct ExplainSinkSchemaPlan {
1186    pub sink_from: GlobalId,
1187    pub json_schema: String,
1188}
1189
1190#[derive(Debug)]
1191pub struct SendDiffsPlan {
1192    pub id: CatalogItemId,
1193    pub updates: Vec<(Row, Diff)>,
1194    pub kind: MutationKind,
1195    pub returning: Vec<(Row, NonZeroUsize)>,
1196    pub max_result_size: u64,
1197}
1198
1199#[derive(Debug)]
1200pub struct InsertPlan {
1201    pub id: CatalogItemId,
1202    pub values: HirRelationExpr,
1203    pub returning: Vec<mz_expr::MirScalarExpr>,
1204}
1205
1206#[derive(Debug)]
1207pub struct ReadThenWritePlan {
1208    pub id: CatalogItemId,
1209    pub selection: HirRelationExpr,
1210    pub finishing: RowSetFinishing,
1211    pub assignments: BTreeMap<usize, mz_expr::MirScalarExpr>,
1212    pub kind: MutationKind,
1213    pub returning: Vec<mz_expr::MirScalarExpr>,
1214}
1215
1216/// Generated by `ALTER ... IF EXISTS` if the named object did not exist.
1217#[derive(Debug)]
1218pub struct AlterNoopPlan {
1219    pub object_type: ObjectType,
1220}
1221
1222#[derive(Debug)]
1223pub struct AlterSetClusterPlan {
1224    pub id: CatalogItemId,
1225    pub set_cluster: ClusterId,
1226}
1227
1228#[derive(Debug)]
1229pub struct AlterRetainHistoryPlan {
1230    pub id: CatalogItemId,
1231    pub value: Option<Value>,
1232    pub window: CompactionWindow,
1233    pub object_type: ObjectType,
1234}
1235
1236#[derive(Debug)]
1237pub struct AlterSourceTimestampIntervalPlan {
1238    pub id: CatalogItemId,
1239    pub value: Option<Value>,
1240    pub interval: Duration,
1241}
1242
1243#[derive(Debug, Clone)]
1244
1245pub enum AlterOptionParameter<T = String> {
1246    Set(T),
1247    Reset,
1248    Unchanged,
1249}
1250
1251#[derive(Debug)]
1252pub enum AlterConnectionAction {
1253    RotateKeys,
1254    AlterOptions {
1255        set_options: BTreeMap<ConnectionOptionName, Option<WithOptionValue<Aug>>>,
1256        drop_options: BTreeSet<ConnectionOptionName>,
1257        validate: bool,
1258    },
1259}
1260
1261#[derive(Debug)]
1262pub struct AlterConnectionPlan {
1263    pub id: CatalogItemId,
1264    pub action: AlterConnectionAction,
1265}
1266
1267#[derive(Debug)]
1268pub enum AlterSourceAction {
1269    AddSubsourceExports {
1270        subsources: Vec<CreateSourcePlanBundle>,
1271        options: Vec<AlterSourceAddSubsourceOption<Aug>>,
1272    },
1273    RefreshReferences {
1274        references: SourceReferences,
1275    },
1276}
1277
1278#[derive(Debug)]
1279pub struct AlterSourcePlan {
1280    pub item_id: CatalogItemId,
1281    pub ingestion_id: GlobalId,
1282    pub action: AlterSourceAction,
1283}
1284
1285#[derive(Debug, Clone)]
1286pub struct AlterSinkPlan {
1287    pub item_id: CatalogItemId,
1288    pub global_id: GlobalId,
1289    pub sink: Sink,
1290    pub with_snapshot: bool,
1291    pub in_cluster: ClusterId,
1292    /// The with-option edit requested by the `ALTER SINK`. Sequencing must
1293    /// re-apply it to the catalog's `create_sql` (via
1294    /// [`apply_sink_option_edits`]) because the `create_sql` may have changed
1295    /// since planning, for example due to a schema swap.
1296    pub set_options: Vec<CreateSinkOption<Aug>>,
1297    pub reset_options: Vec<CreateSinkOptionName>,
1298}
1299
1300/// Applies the option edits of an `ALTER SINK ... SET/RESET (...)` to the
1301/// with-options of a `CREATE SINK` statement.
1302pub fn apply_sink_option_edits<T: mz_sql_parser::ast::AstInfo>(
1303    with_options: &mut Vec<CreateSinkOption<T>>,
1304    set_options: &[CreateSinkOption<T>],
1305    reset_options: &[CreateSinkOptionName],
1306) where
1307    CreateSinkOption<T>: Clone,
1308{
1309    with_options.retain(|o| {
1310        set_options.iter().all(|s| s.name != o.name) && !reset_options.contains(&o.name)
1311    });
1312    with_options.extend(set_options.iter().cloned());
1313}
1314
1315#[derive(Debug, Clone)]
1316pub struct AlterClusterPlan {
1317    pub id: ClusterId,
1318    pub name: String,
1319    pub options: PlanClusterOption,
1320    pub strategy: AlterClusterPlanStrategy,
1321}
1322
1323#[derive(Debug)]
1324pub struct AlterClusterRenamePlan {
1325    pub id: ClusterId,
1326    pub name: String,
1327    pub to_name: String,
1328}
1329
1330#[derive(Debug)]
1331pub struct AlterClusterReplicaRenamePlan {
1332    pub cluster_id: ClusterId,
1333    pub replica_id: ReplicaId,
1334    pub name: QualifiedReplica,
1335    pub to_name: String,
1336}
1337
1338#[derive(Debug)]
1339pub struct AlterItemRenamePlan {
1340    pub id: CatalogItemId,
1341    pub current_full_name: FullItemName,
1342    pub to_name: String,
1343    pub object_type: ObjectType,
1344}
1345
1346#[derive(Debug)]
1347pub struct AlterSchemaRenamePlan {
1348    pub cur_schema_spec: (ResolvedDatabaseSpecifier, SchemaSpecifier),
1349    pub new_schema_name: String,
1350}
1351
1352#[derive(Debug)]
1353pub struct AlterSchemaSwapPlan {
1354    pub schema_a_spec: (ResolvedDatabaseSpecifier, SchemaSpecifier),
1355    pub schema_a_name: String,
1356    pub schema_b_spec: (ResolvedDatabaseSpecifier, SchemaSpecifier),
1357    pub schema_b_name: String,
1358    pub name_temp: String,
1359}
1360
1361#[derive(Debug)]
1362pub struct AlterClusterSwapPlan {
1363    pub id_a: ClusterId,
1364    pub id_b: ClusterId,
1365    pub name_a: String,
1366    pub name_b: String,
1367    pub name_temp: String,
1368}
1369
1370#[derive(Debug)]
1371pub struct AlterSecretPlan {
1372    pub id: CatalogItemId,
1373    pub secret_as: MirScalarExpr,
1374}
1375
1376#[derive(Debug)]
1377pub struct AlterSystemSetPlan {
1378    pub name: String,
1379    pub value: VariableValue,
1380}
1381
1382#[derive(Debug)]
1383pub struct AlterSystemResetPlan {
1384    pub name: String,
1385}
1386
1387#[derive(Debug)]
1388pub struct AlterSystemResetAllPlan {}
1389
1390#[derive(Debug)]
1391pub struct AlterRolePlan {
1392    pub id: RoleId,
1393    pub name: String,
1394    pub option: PlannedAlterRoleOption,
1395}
1396
1397#[derive(Debug)]
1398pub struct AlterOwnerPlan {
1399    pub id: ObjectId,
1400    pub object_type: ObjectType,
1401    pub new_owner: RoleId,
1402}
1403
1404#[derive(Debug)]
1405pub struct AlterTablePlan {
1406    pub relation_id: CatalogItemId,
1407    pub column_name: ColumnName,
1408    pub column_type: SqlColumnType,
1409    pub raw_sql_type: RawDataType,
1410}
1411
1412#[derive(Debug, Clone)]
1413pub struct AlterMaterializedViewApplyReplacementPlan {
1414    pub id: CatalogItemId,
1415    pub replacement_id: CatalogItemId,
1416}
1417
1418#[derive(Debug)]
1419pub struct DeclarePlan {
1420    pub name: String,
1421    pub stmt: Statement<Raw>,
1422    pub sql: String,
1423    pub params: Params,
1424}
1425
1426#[derive(Debug)]
1427pub struct FetchPlan {
1428    pub name: String,
1429    pub count: Option<FetchDirection>,
1430    pub timeout: ExecuteTimeout,
1431}
1432
1433#[derive(Debug)]
1434pub struct ClosePlan {
1435    pub name: String,
1436}
1437
1438#[derive(Debug)]
1439pub struct PreparePlan {
1440    pub name: String,
1441    pub stmt: Statement<Raw>,
1442    pub sql: String,
1443    pub desc: StatementDesc,
1444}
1445
1446#[derive(Debug)]
1447pub struct ExecutePlan {
1448    pub name: String,
1449    pub params: Params,
1450}
1451
1452#[derive(Debug)]
1453pub struct DeallocatePlan {
1454    pub name: Option<String>,
1455}
1456
1457#[derive(Debug)]
1458pub struct RaisePlan {
1459    pub severity: NoticeSeverity,
1460}
1461
1462#[derive(Debug)]
1463pub struct GrantRolePlan {
1464    /// The roles that are gaining members.
1465    pub role_ids: Vec<RoleId>,
1466    /// The roles that will be added to `role_id`.
1467    pub member_ids: Vec<RoleId>,
1468    /// The role that granted the membership.
1469    pub grantor_id: RoleId,
1470}
1471
1472#[derive(Debug)]
1473pub struct RevokeRolePlan {
1474    /// The roles that are losing members.
1475    pub role_ids: Vec<RoleId>,
1476    /// The roles that will be removed from `role_id`.
1477    pub member_ids: Vec<RoleId>,
1478    /// The role that revoked the membership.
1479    pub grantor_id: RoleId,
1480}
1481
1482#[derive(Debug)]
1483pub struct UpdatePrivilege {
1484    /// The privileges being granted/revoked on an object.
1485    pub acl_mode: AclMode,
1486    /// The ID of the object receiving privileges.
1487    pub target_id: SystemObjectId,
1488    /// The role that is granting the privileges.
1489    pub grantor: RoleId,
1490    /// Whether `acl_mode` was derived from the `ALL [PRIVILEGES]` shorthand.
1491    /// Used to suppress the `NonApplicablePrivilegeTypes` notice in that
1492    /// case: the shorthand is not the user explicitly naming a privilege
1493    /// that doesn't apply to the object type, so warning would be noisy.
1494    pub acl_from_all: bool,
1495}
1496
1497#[derive(Debug)]
1498pub struct GrantPrivilegesPlan {
1499    /// Description of each privilege being granted.
1500    pub update_privileges: Vec<UpdatePrivilege>,
1501    /// The roles that will granted the privileges.
1502    pub grantees: Vec<RoleId>,
1503}
1504
1505#[derive(Debug)]
1506pub struct RevokePrivilegesPlan {
1507    /// Description of each privilege being revoked.
1508    pub update_privileges: Vec<UpdatePrivilege>,
1509    /// The roles that will have privileges revoked.
1510    pub revokees: Vec<RoleId>,
1511}
1512#[derive(Debug)]
1513pub struct AlterDefaultPrivilegesPlan {
1514    /// Description of objects that match this default privilege.
1515    pub privilege_objects: Vec<DefaultPrivilegeObject>,
1516    /// The privilege to be granted/revoked from the matching objects.
1517    pub privilege_acl_items: Vec<DefaultPrivilegeAclItem>,
1518    /// Whether this is a grant or revoke.
1519    pub is_grant: bool,
1520}
1521
1522#[derive(Debug)]
1523pub struct ReassignOwnedPlan {
1524    /// The roles whose owned objects are being reassigned.
1525    pub old_roles: Vec<RoleId>,
1526    /// The new owner of the objects.
1527    pub new_role: RoleId,
1528    /// All object IDs to reassign.
1529    pub reassign_ids: Vec<ObjectId>,
1530}
1531
1532#[derive(Debug)]
1533pub struct CommentPlan {
1534    /// The object that this comment is associated with.
1535    pub object_id: CommentObjectId,
1536    /// A sub-component of the object that this comment is associated with, e.g. a column.
1537    ///
1538    /// TODO(parkmycar): <https://github.com/MaterializeInc/database-issues/issues/6711>.
1539    pub sub_component: Option<usize>,
1540    /// The comment itself. If `None` that indicates we should clear the existing comment.
1541    pub comment: Option<String>,
1542}
1543
1544#[derive(Clone, Debug)]
1545pub enum TableDataSource {
1546    /// The table owns data created via INSERT/UPDATE/DELETE statements.
1547    TableWrites { defaults: Vec<Expr<Aug>> },
1548
1549    /// The table receives its data from the identified `DataSourceDesc`.
1550    /// This table type does not support INSERT/UPDATE/DELETE statements.
1551    DataSource {
1552        desc: DataSourceDesc,
1553        timeline: Timeline,
1554    },
1555}
1556
1557#[derive(Clone, Debug)]
1558pub struct Table {
1559    pub create_sql: String,
1560    pub desc: VersionedRelationDesc,
1561    pub temporary: bool,
1562    pub compaction_window: Option<CompactionWindow>,
1563    pub data_source: TableDataSource,
1564}
1565
1566#[derive(Clone, Debug)]
1567pub struct Source {
1568    pub create_sql: String,
1569    pub data_source: DataSourceDesc,
1570    pub desc: RelationDesc,
1571    pub compaction_window: Option<CompactionWindow>,
1572}
1573
1574#[derive(Debug, Clone)]
1575pub enum DataSourceDesc {
1576    /// Receives data from an external system.
1577    Ingestion(SourceDesc<ReferencedConnection>),
1578    /// Receives data from an external system.
1579    OldSyntaxIngestion {
1580        desc: SourceDesc<ReferencedConnection>,
1581        // If we're dealing with an old syntax ingestion the progress id will be some other collection
1582        // and the ingestion itself will have the data from a default external reference
1583        progress_subsource: CatalogItemId,
1584        data_config: SourceExportDataConfig<ReferencedConnection>,
1585        details: SourceExportDetails,
1586    },
1587    /// This source receives its data from the identified ingestion,
1588    /// specifically the output identified by `external_reference`.
1589    IngestionExport {
1590        ingestion_id: CatalogItemId,
1591        external_reference: UnresolvedItemName,
1592        details: SourceExportDetails,
1593        data_config: SourceExportDataConfig<ReferencedConnection>,
1594    },
1595    /// Receives data from the source's reclocking/remapping operations.
1596    Progress,
1597    /// Receives data from HTTP post requests.
1598    Webhook {
1599        validate_using: Option<WebhookValidation>,
1600        body_format: WebhookBodyFormat,
1601        headers: WebhookHeaders,
1602        /// Only `Some` when created via `CREATE TABLE ... FROM WEBHOOK`.
1603        cluster_id: Option<StorageInstanceId>,
1604    },
1605}
1606
1607#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
1608pub struct WebhookValidation {
1609    /// The expression used to validate a request.
1610    pub expression: MirScalarExpr,
1611    /// Description of the source that will be created.
1612    pub relation_desc: RelationDesc,
1613    /// The column index to provide the request body and whether to provide it as bytes.
1614    pub bodies: Vec<(usize, bool)>,
1615    /// The column index to provide the request headers and whether to provide the values as bytes.
1616    pub headers: Vec<(usize, bool)>,
1617    /// Any secrets that are used in that validation.
1618    pub secrets: Vec<WebhookValidationSecret>,
1619}
1620
1621impl WebhookValidation {
1622    const MAX_REDUCE_TIME: Duration = Duration::from_secs(60);
1623
1624    /// Attempt to reduce the internal [`MirScalarExpr`] into a simpler expression.
1625    ///
1626    /// The reduction happens on a separate thread, we also only wait for
1627    /// `WebhookValidation::MAX_REDUCE_TIME` before timing out and returning an error.
1628    pub async fn reduce_expression(&mut self) -> Result<(), &'static str> {
1629        let WebhookValidation {
1630            expression,
1631            relation_desc,
1632            ..
1633        } = self;
1634
1635        // On a different thread, attempt to reduce the expression.
1636        let mut expression_ = expression.clone();
1637        let desc_ = relation_desc.clone();
1638        let reduce_task = mz_ore::task::spawn_blocking(
1639            || "webhook-validation-reduce",
1640            move || {
1641                let repr_col_types: Vec<ReprColumnType> = desc_
1642                    .typ()
1643                    .column_types
1644                    .iter()
1645                    .map(ReprColumnType::from)
1646                    .collect();
1647                expression_.reduce(&repr_col_types);
1648                expression_
1649            },
1650        );
1651
1652        match tokio::time::timeout(Self::MAX_REDUCE_TIME, reduce_task).await {
1653            Ok(reduced_expr) => {
1654                *expression = reduced_expr;
1655                Ok(())
1656            }
1657            Err(_) => Err("timeout"),
1658        }
1659    }
1660}
1661
1662#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
1663pub struct WebhookHeaders {
1664    /// Optionally include a column named `headers` whose content is possibly filtered.
1665    pub header_column: Option<WebhookHeaderFilters>,
1666    /// The column index to provide the specific request header, and whether to provide it as bytes.
1667    pub mapped_headers: BTreeMap<usize, (String, bool)>,
1668}
1669
1670impl WebhookHeaders {
1671    /// Returns the number of columns needed to represent our headers.
1672    pub fn num_columns(&self) -> usize {
1673        let header_column = self.header_column.as_ref().map(|_| 1).unwrap_or(0);
1674        let mapped_headers = self.mapped_headers.len();
1675
1676        header_column + mapped_headers
1677    }
1678}
1679
1680#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
1681pub struct WebhookHeaderFilters {
1682    pub block: BTreeSet<String>,
1683    pub allow: BTreeSet<String>,
1684}
1685
1686#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Arbitrary)]
1687pub enum WebhookBodyFormat {
1688    Json { array: bool },
1689    Bytes,
1690    Text,
1691}
1692
1693impl From<WebhookBodyFormat> for SqlScalarType {
1694    fn from(value: WebhookBodyFormat) -> Self {
1695        match value {
1696            WebhookBodyFormat::Json { .. } => SqlScalarType::Jsonb,
1697            WebhookBodyFormat::Bytes => SqlScalarType::Bytes,
1698            WebhookBodyFormat::Text => SqlScalarType::String,
1699        }
1700    }
1701}
1702
1703#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
1704pub struct WebhookValidationSecret {
1705    /// Identifies the secret by [`CatalogItemId`].
1706    pub id: CatalogItemId,
1707    /// Column index for the expression context that this secret was originally evaluated in.
1708    pub column_idx: usize,
1709    /// Whether or not this secret should be provided to the expression as Bytes or a String.
1710    pub use_bytes: bool,
1711}
1712
1713#[derive(Clone, Debug)]
1714pub struct Connection {
1715    pub create_sql: String,
1716    pub details: ConnectionDetails,
1717}
1718
1719#[derive(Clone, Debug, Serialize)]
1720pub enum ConnectionDetails {
1721    Kafka(KafkaConnection<ReferencedConnection>),
1722    Csr(CsrConnection<ReferencedConnection>),
1723    GlueSchemaRegistry(GlueSchemaRegistryConnection<ReferencedConnection>),
1724    Postgres(PostgresConnection<ReferencedConnection>),
1725    Ssh {
1726        connection: SshConnection,
1727        key_1: SshKey,
1728        key_2: SshKey,
1729    },
1730    Aws(AwsConnection),
1731    AwsPrivatelink(AwsPrivatelinkConnection),
1732    Gcp(GcpConnection),
1733    MySql(MySqlConnection<ReferencedConnection>),
1734    SqlServer(SqlServerConnectionDetails<ReferencedConnection>),
1735    IcebergCatalog(IcebergCatalogConnection<ReferencedConnection>),
1736}
1737
1738impl ConnectionDetails {
1739    pub fn to_connection(&self) -> mz_storage_types::connections::Connection<ReferencedConnection> {
1740        match self {
1741            ConnectionDetails::Kafka(c) => {
1742                mz_storage_types::connections::Connection::Kafka(c.clone())
1743            }
1744            ConnectionDetails::Csr(c) => mz_storage_types::connections::Connection::Csr(c.clone()),
1745            ConnectionDetails::GlueSchemaRegistry(c) => {
1746                mz_storage_types::connections::Connection::GlueSchemaRegistry(c.clone())
1747            }
1748            ConnectionDetails::Postgres(c) => {
1749                mz_storage_types::connections::Connection::Postgres(c.clone())
1750            }
1751            ConnectionDetails::Ssh { connection, .. } => {
1752                mz_storage_types::connections::Connection::Ssh(connection.clone())
1753            }
1754            ConnectionDetails::Aws(c) => mz_storage_types::connections::Connection::Aws(c.clone()),
1755            ConnectionDetails::AwsPrivatelink(c) => {
1756                mz_storage_types::connections::Connection::AwsPrivatelink(c.clone())
1757            }
1758            ConnectionDetails::Gcp(c) => mz_storage_types::connections::Connection::Gcp(c.clone()),
1759            ConnectionDetails::MySql(c) => {
1760                mz_storage_types::connections::Connection::MySql(c.clone())
1761            }
1762            ConnectionDetails::SqlServer(c) => {
1763                mz_storage_types::connections::Connection::SqlServer(c.clone())
1764            }
1765            ConnectionDetails::IcebergCatalog(c) => {
1766                mz_storage_types::connections::Connection::IcebergCatalog(c.clone())
1767            }
1768        }
1769    }
1770
1771    /// Secrets whose *contents* this connection places requirements on, paired
1772    /// with the check to apply. Callers must re-apply these checks whenever the
1773    /// connection is created or altered, and whenever the contents of one of
1774    /// the returned secrets change (e.g. `ALTER SECRET`).
1775    ///
1776    /// We rely on the caller to actually execute these checks because we don't know:
1777    /// - which secrets the caller cares about
1778    /// - which secrets require an async operation to fetch
1779    ///
1780    /// For example, the ALTER SECRET caller should only perform checks on its own secret,
1781    /// while the ALTER CONNECTION caller fetches and checks every secret from its connection.
1782    pub fn secret_content_guards(
1783        &self,
1784    ) -> Vec<(CatalogItemId, fn(&str) -> Result<(), anyhow::Error>)> {
1785        match self {
1786            // A service-account key defines its own OAuth2 token URI. We only
1787            // want to send requests to the actual Google OAuth2 token API.
1788            ConnectionDetails::Gcp(gcp) => vec![(
1789                gcp.credentials_json,
1790                GcpServiceAccountKeyTokenUri::validate_json,
1791            )],
1792            _ => vec![],
1793        }
1794    }
1795}
1796
1797#[derive(Debug, Clone, Serialize, PartialEq, Eq, Ord, PartialOrd, Hash)]
1798pub struct NetworkPolicyRule {
1799    pub name: String,
1800    pub action: NetworkPolicyRuleAction,
1801    pub address: PolicyAddress,
1802    pub direction: NetworkPolicyRuleDirection,
1803}
1804
1805#[derive(
1806    Debug,
1807    Clone,
1808    Serialize,
1809    Deserialize,
1810    PartialEq,
1811    Eq,
1812    Ord,
1813    PartialOrd,
1814    Hash
1815)]
1816pub enum NetworkPolicyRuleAction {
1817    Allow,
1818}
1819
1820impl std::fmt::Display for NetworkPolicyRuleAction {
1821    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1822        match self {
1823            Self::Allow => write!(f, "allow"),
1824        }
1825    }
1826}
1827impl TryFrom<&str> for NetworkPolicyRuleAction {
1828    type Error = PlanError;
1829    fn try_from(value: &str) -> Result<Self, Self::Error> {
1830        match value.to_uppercase().as_str() {
1831            "ALLOW" => Ok(Self::Allow),
1832            _ => Err(PlanError::Unstructured(
1833                "Allow is the only valid option".into(),
1834            )),
1835        }
1836    }
1837}
1838
1839#[derive(Debug, Clone, Serialize, PartialEq, Eq, Ord, PartialOrd, Hash)]
1840pub enum NetworkPolicyRuleDirection {
1841    Ingress,
1842}
1843impl std::fmt::Display for NetworkPolicyRuleDirection {
1844    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1845        match self {
1846            Self::Ingress => write!(f, "ingress"),
1847        }
1848    }
1849}
1850impl TryFrom<&str> for NetworkPolicyRuleDirection {
1851    type Error = PlanError;
1852    fn try_from(value: &str) -> Result<Self, Self::Error> {
1853        match value.to_uppercase().as_str() {
1854            "INGRESS" => Ok(Self::Ingress),
1855            _ => Err(PlanError::Unstructured(
1856                "Ingress is the only valid option".into(),
1857            )),
1858        }
1859    }
1860}
1861
1862#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1863pub struct PolicyAddress(pub IpNet);
1864impl std::fmt::Display for PolicyAddress {
1865    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1866        write!(f, "{}", &self.0.to_string())
1867    }
1868}
1869impl From<String> for PolicyAddress {
1870    fn from(value: String) -> Self {
1871        Self(IpNet::from_str(&value).expect("expected value to be IpNet"))
1872    }
1873}
1874impl TryFrom<&str> for PolicyAddress {
1875    type Error = PlanError;
1876    fn try_from(value: &str) -> Result<Self, Self::Error> {
1877        let net = IpNet::from_str(value)
1878            .map_err(|_| PlanError::Unstructured("Value must be valid IPV4 or IPV6 CIDR".into()))?;
1879        Ok(Self(net))
1880    }
1881}
1882
1883impl Serialize for PolicyAddress {
1884    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1885    where
1886        S: serde::Serializer,
1887    {
1888        serializer.serialize_str(&format!("{}", &self.0))
1889    }
1890}
1891
1892#[derive(Clone, Debug, Serialize)]
1893pub enum SshKey {
1894    PublicOnly(String),
1895    Both(SshKeyPair),
1896}
1897
1898impl SshKey {
1899    pub fn as_key_pair(&self) -> Option<&SshKeyPair> {
1900        match self {
1901            SshKey::PublicOnly(_) => None,
1902            SshKey::Both(key_pair) => Some(key_pair),
1903        }
1904    }
1905
1906    pub fn public_key(&self) -> String {
1907        match self {
1908            SshKey::PublicOnly(s) => s.into(),
1909            SshKey::Both(p) => p.ssh_public_key(),
1910        }
1911    }
1912}
1913
1914#[derive(Clone, Debug)]
1915pub struct Secret {
1916    pub create_sql: String,
1917    pub secret_as: MirScalarExpr,
1918}
1919
1920#[derive(Clone, Debug)]
1921pub struct Sink {
1922    /// Parse-able SQL that is stored durably and defines this sink.
1923    pub create_sql: String,
1924    /// Collection we read into this sink.
1925    pub from: GlobalId,
1926    /// Type of connection to the external service we sink into.
1927    pub connection: StorageSinkConnection<ReferencedConnection>,
1928    // TODO(guswynn): this probably should just be in the `connection`.
1929    pub envelope: SinkEnvelope,
1930    pub version: u64,
1931    pub commit_interval: Option<Duration>,
1932}
1933
1934#[derive(Clone, Debug)]
1935pub struct View {
1936    /// Parse-able SQL that is stored durably and defines this view.
1937    pub create_sql: String,
1938    /// Unoptimized high-level expression from parsing the `create_sql`.
1939    pub expr: HirRelationExpr,
1940    /// All of the catalog objects that are referenced by this view, according to the `expr`.
1941    pub dependencies: DependencyIds,
1942    /// Columns of this view.
1943    pub column_names: Vec<ColumnName>,
1944    /// If this view is created in the temporary schema, e.g. `CREATE TEMPORARY ...`.
1945    pub temporary: bool,
1946}
1947
1948#[derive(Clone, Debug)]
1949pub struct MaterializedView {
1950    /// Parse-able SQL that is stored durably and defines this materialized view.
1951    pub create_sql: String,
1952    /// Unoptimized high-level expression from parsing the `create_sql`.
1953    pub expr: HirRelationExpr,
1954    /// All of the catalog objects that are referenced by this materialized view, according to the `expr`.
1955    pub dependencies: DependencyIds,
1956    /// Columns of this view.
1957    pub column_names: Vec<ColumnName>,
1958    pub replacement_target: Option<CatalogItemId>,
1959    /// Cluster this materialized view will get installed on.
1960    pub cluster_id: ClusterId,
1961    /// If set, only install this materialized view's dataflow on the specified replica.
1962    pub target_replica: Option<ReplicaId>,
1963    pub non_null_assertions: Vec<usize>,
1964    pub compaction_window: Option<CompactionWindow>,
1965    pub refresh_schedule: Option<RefreshSchedule>,
1966    pub as_of: Option<Timestamp>,
1967}
1968
1969#[derive(Clone, Debug)]
1970pub struct Index {
1971    /// Parse-able SQL that is stored durably and defines this index.
1972    pub create_sql: String,
1973    /// Collection this index is on top of.
1974    pub on: GlobalId,
1975    pub keys: Vec<mz_expr::MirScalarExpr>,
1976    pub compaction_window: Option<CompactionWindow>,
1977    pub cluster_id: ClusterId,
1978}
1979
1980#[derive(Clone, Debug)]
1981pub struct Type {
1982    pub create_sql: String,
1983    pub inner: CatalogType<IdReference>,
1984}
1985
1986/// Specifies when a `Peek` or `Subscribe` should occur.
1987#[derive(Deserialize, Clone, Debug, PartialEq)]
1988pub enum QueryWhen {
1989    /// The peek should occur at the latest possible timestamp that allows the
1990    /// peek to complete immediately.
1991    Immediately,
1992    /// The peek should occur at a timestamp that allows the peek to see all
1993    /// data written to tables within Materialize.
1994    FreshestTableWrite,
1995    /// The peek should occur at the timestamp described by the specified
1996    /// expression.
1997    ///
1998    /// The expression may have any type.
1999    AtTimestamp(Timestamp),
2000    /// Same as Immediately, but will also advance to at least the specified
2001    /// expression.
2002    AtLeastTimestamp(Timestamp),
2003}
2004
2005impl QueryWhen {
2006    /// Returns a timestamp to which the candidate must be advanced.
2007    pub fn advance_to_timestamp(&self) -> Option<Timestamp> {
2008        match self {
2009            QueryWhen::AtTimestamp(t) | QueryWhen::AtLeastTimestamp(t) => Some(t.clone()),
2010            QueryWhen::Immediately | QueryWhen::FreshestTableWrite => None,
2011        }
2012    }
2013    /// Returns whether the candidate's upper bound is constrained.
2014    /// This is only true for `AtTimestamp` since it is the only variant that
2015    /// specifies a timestamp.
2016    pub fn constrains_upper(&self) -> bool {
2017        match self {
2018            QueryWhen::AtTimestamp(_) => true,
2019            QueryWhen::AtLeastTimestamp(_)
2020            | QueryWhen::Immediately
2021            | QueryWhen::FreshestTableWrite => false,
2022        }
2023    }
2024    /// Returns whether the candidate must be advanced to the since.
2025    pub fn advance_to_since(&self) -> bool {
2026        match self {
2027            QueryWhen::Immediately
2028            | QueryWhen::AtLeastTimestamp(_)
2029            | QueryWhen::FreshestTableWrite => true,
2030            QueryWhen::AtTimestamp(_) => false,
2031        }
2032    }
2033    /// Returns whether the candidate can be advanced to the upper.
2034    pub fn can_advance_to_upper(&self) -> bool {
2035        match self {
2036            QueryWhen::Immediately => true,
2037            QueryWhen::FreshestTableWrite
2038            | QueryWhen::AtTimestamp(_)
2039            | QueryWhen::AtLeastTimestamp(_) => false,
2040        }
2041    }
2042
2043    /// Returns whether the candidate can be advanced to the timeline's timestamp.
2044    pub fn can_advance_to_timeline_ts(&self) -> bool {
2045        match self {
2046            QueryWhen::Immediately | QueryWhen::FreshestTableWrite => true,
2047            QueryWhen::AtTimestamp(_) | QueryWhen::AtLeastTimestamp(_) => false,
2048        }
2049    }
2050    /// Returns whether the candidate must be advanced to the timeline's timestamp.
2051    pub fn must_advance_to_timeline_ts(&self) -> bool {
2052        match self {
2053            QueryWhen::FreshestTableWrite => true,
2054            QueryWhen::Immediately | QueryWhen::AtLeastTimestamp(_) | QueryWhen::AtTimestamp(_) => {
2055                false
2056            }
2057        }
2058    }
2059    /// Returns whether the selected timestamp should be tracked within the current transaction.
2060    pub fn is_transactional(&self) -> bool {
2061        match self {
2062            QueryWhen::Immediately | QueryWhen::FreshestTableWrite => true,
2063            QueryWhen::AtLeastTimestamp(_) | QueryWhen::AtTimestamp(_) => false,
2064        }
2065    }
2066}
2067
2068#[derive(Debug, Copy, Clone)]
2069pub enum MutationKind {
2070    Insert,
2071    Update,
2072    Delete,
2073}
2074
2075#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
2076pub enum CopyFormat {
2077    Text,
2078    Csv,
2079    Binary,
2080    Parquet,
2081}
2082
2083#[derive(Debug, Copy, Clone)]
2084pub enum ExecuteTimeout {
2085    None,
2086    Seconds(f64),
2087    WaitOnce,
2088}
2089
2090#[derive(Clone, Debug)]
2091pub enum IndexOption {
2092    /// Configures the logical compaction window for an index.
2093    RetainHistory(CompactionWindow),
2094}
2095
2096#[derive(Clone, Debug)]
2097pub enum TableOption {
2098    /// Configures the logical compaction window for a table.
2099    RetainHistory(CompactionWindow),
2100}
2101
2102#[derive(Clone, Debug)]
2103pub struct PlanClusterOption {
2104    pub availability_zones: AlterOptionParameter<Vec<String>>,
2105    pub introspection_debugging: AlterOptionParameter<bool>,
2106    pub introspection_interval: AlterOptionParameter<OptionalDuration>,
2107    pub arrangement_compression: AlterOptionParameter<bool>,
2108    pub managed: AlterOptionParameter<bool>,
2109    pub replicas: AlterOptionParameter<Vec<(String, ReplicaConfig)>>,
2110    pub replication_factor: AlterOptionParameter<u32>,
2111    pub size: AlterOptionParameter,
2112    pub schedule: AlterOptionParameter<ClusterSchedule>,
2113    pub workload_class: AlterOptionParameter<Option<String>>,
2114    /// The autoscaling policy block. `Set(None)` disables autoscaling (an empty
2115    /// `AUTO SCALING STRATEGY = ()` or `RESET (AUTO SCALING STRATEGY)`).
2116    pub auto_scaling_strategy: AlterOptionParameter<Option<AutoScalingStrategy>>,
2117}
2118
2119impl Default for PlanClusterOption {
2120    fn default() -> Self {
2121        Self {
2122            availability_zones: AlterOptionParameter::Unchanged,
2123            introspection_debugging: AlterOptionParameter::Unchanged,
2124            introspection_interval: AlterOptionParameter::Unchanged,
2125            arrangement_compression: AlterOptionParameter::Unchanged,
2126            managed: AlterOptionParameter::Unchanged,
2127            replicas: AlterOptionParameter::Unchanged,
2128            replication_factor: AlterOptionParameter::Unchanged,
2129            size: AlterOptionParameter::Unchanged,
2130            schedule: AlterOptionParameter::Unchanged,
2131            workload_class: AlterOptionParameter::Unchanged,
2132            auto_scaling_strategy: AlterOptionParameter::Unchanged,
2133        }
2134    }
2135}
2136
2137#[derive(Clone, Debug, PartialEq, Eq)]
2138pub enum AlterClusterPlanStrategy {
2139    None,
2140    For(Duration),
2141    UntilReady {
2142        /// `None` when the `ALTER` omits `ON TIMEOUT`. The executing path
2143        /// supplies the implicit action.
2144        on_timeout: Option<OnTimeoutAction>,
2145        timeout: Duration,
2146    },
2147}
2148
2149#[derive(
2150    Clone,
2151    Copy,
2152    Debug,
2153    Deserialize,
2154    Serialize,
2155    PartialOrd,
2156    PartialEq,
2157    Eq,
2158    Ord
2159)]
2160pub enum OnTimeoutAction {
2161    /// Cut over to the target shape even though it has not hydrated.
2162    Commit,
2163    /// Drop the target replicas and keep the pre-reconfiguration set.
2164    Rollback,
2165}
2166
2167impl TryFrom<&str> for OnTimeoutAction {
2168    type Error = PlanError;
2169    fn try_from(value: &str) -> Result<Self, Self::Error> {
2170        match value.to_uppercase().as_str() {
2171            "COMMIT" => Ok(Self::Commit),
2172            "ROLLBACK" => Ok(Self::Rollback),
2173            _ => Err(PlanError::Unstructured(
2174                "Valid options are COMMIT, ROLLBACK".into(),
2175            )),
2176        }
2177    }
2178}
2179
2180impl AlterClusterPlanStrategy {
2181    pub fn is_none(&self) -> bool {
2182        matches!(self, Self::None)
2183    }
2184    pub fn is_some(&self) -> bool {
2185        !matches!(self, Self::None)
2186    }
2187}
2188
2189impl TryFrom<ClusterAlterOptionExtracted> for AlterClusterPlanStrategy {
2190    type Error = PlanError;
2191
2192    fn try_from(value: ClusterAlterOptionExtracted) -> Result<Self, Self::Error> {
2193        Ok(match value.wait {
2194            Some(ClusterAlterOptionValue::For(d)) => Self::For(Duration::try_from_value(d)?),
2195            Some(ClusterAlterOptionValue::UntilReady(options)) => {
2196                let extracted = ClusterAlterUntilReadyOptionExtracted::try_from(options)?;
2197                Self::UntilReady {
2198                    timeout: match extracted.timeout {
2199                        Some(d) => d,
2200                        None => Err(PlanError::UntilReadyTimeoutRequired)?,
2201                    },
2202                    on_timeout: match extracted.on_timeout {
2203                        Some(v) => Some(OnTimeoutAction::try_from(v.as_str()).map_err(|e| {
2204                            PlanError::InvalidOptionValue {
2205                                option_name: "ON TIMEOUT".into(),
2206                                err: Box::new(e),
2207                            }
2208                        })?),
2209                        None => None,
2210                    },
2211                }
2212            }
2213            None => Self::None,
2214        })
2215    }
2216}
2217
2218/// A vector of values to which parameter references should be bound.
2219#[derive(Debug, Clone)]
2220pub struct Params {
2221    /// The datums that were provided in the EXECUTE statement.
2222    pub datums: Row,
2223    /// The types of the datums provided in the EXECUTE statement.
2224    pub execute_types: Vec<SqlScalarType>,
2225    /// The types that the prepared statement expects based on its definition.
2226    pub expected_types: Vec<SqlScalarType>,
2227}
2228
2229impl Params {
2230    /// Returns a `Params` with no parameters.
2231    pub fn empty() -> Params {
2232        Params {
2233            datums: Row::pack_slice(&[]),
2234            execute_types: vec![],
2235            expected_types: vec![],
2236        }
2237    }
2238}
2239
2240/// Controls planning of a SQL query.
2241#[derive(
2242    Ord,
2243    PartialOrd,
2244    Clone,
2245    Debug,
2246    Eq,
2247    PartialEq,
2248    Serialize,
2249    Deserialize,
2250    Hash,
2251    Copy
2252)]
2253pub struct PlanContext {
2254    pub wall_time: DateTime<Utc>,
2255    pub ignore_if_exists_errors: bool,
2256}
2257
2258impl PlanContext {
2259    pub fn new(wall_time: DateTime<Utc>) -> Self {
2260        Self {
2261            wall_time,
2262            ignore_if_exists_errors: false,
2263        }
2264    }
2265
2266    /// Return a PlanContext with zero values. This should only be used when
2267    /// planning is required but unused (like in `plan_create_table()`) or in
2268    /// tests.
2269    pub fn zero() -> Self {
2270        PlanContext {
2271            wall_time: now::to_datetime(NOW_ZERO()),
2272            ignore_if_exists_errors: false,
2273        }
2274    }
2275
2276    pub fn with_ignore_if_exists_errors(mut self, value: bool) -> Self {
2277        self.ignore_if_exists_errors = value;
2278        self
2279    }
2280}