Skip to main content

mz_sql/
plan.rs

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