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