Skip to main content

mz_sql/
plan.rs

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