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