1use std::cell::RefCell;
15use std::collections::{BTreeMap, BTreeSet};
16
17use mz_repr::namespaces::is_system_schema;
18use mz_repr::{
19 CatalogItemId, ColumnIndex, RelationDesc, RelationVersionSelector, SqlColumnType, SqlScalarType,
20};
21use mz_sql_parser::ast::{
22 ColumnDef, ColumnName, ConnectionDefaultAwsPrivatelink, CreateMaterializedViewStatement,
23 RawItemName, ShowStatement, StatementKind, TableConstraint, UnresolvedDatabaseName,
24 UnresolvedSchemaName,
25};
26use mz_storage_types::connections::inline::ReferencedConnection;
27use mz_storage_types::connections::{AwsPrivatelink, Connection, SshTunnel, Tunnel};
28
29use crate::ast::{Ident, Statement, UnresolvedItemName};
30use crate::catalog::{
31 CatalogCluster, CatalogCollectionItem, CatalogDatabase, CatalogItem, CatalogItemType,
32 CatalogSchema, ObjectType, SessionCatalog, SystemObjectType,
33};
34use crate::names::{
35 self, Aug, DatabaseId, FullItemName, ItemQualifiers, ObjectId, PartialItemName,
36 QualifiedItemName, RawDatabaseSpecifier, ResolvedColumnReference, ResolvedDataType,
37 ResolvedDatabaseSpecifier, ResolvedIds, ResolvedItemName, ResolvedSchemaName, SchemaSpecifier,
38 SystemObjectId,
39};
40use crate::normalize;
41use crate::plan::error::PlanError;
42use crate::plan::{Params, Plan, PlanContext, PlanKind, query, with_options};
43use crate::session::vars::FeatureFlag;
44
45mod acl;
46pub(crate) mod ddl;
47mod dml;
48mod raise;
49mod scl;
50pub(crate) mod show;
51mod tcl;
52mod validate;
53
54use crate::session::vars;
55pub(crate) use ddl::PgConfigOptionExtracted;
56use mz_controller_types::ClusterId;
57use mz_pgrepr::oid::{FIRST_MATERIALIZE_OID, FIRST_USER_OID};
58use mz_repr::role_id::RoleId;
59
60#[derive(Debug, Clone, Eq, PartialEq)]
62pub struct StatementDesc {
63 pub relation_desc: Option<RelationDesc>,
66 pub param_types: Vec<SqlScalarType>,
68 pub is_copy: bool,
70}
71
72impl StatementDesc {
73 pub fn new(relation_desc: Option<RelationDesc>) -> Self {
74 StatementDesc {
75 relation_desc,
76 param_types: vec![],
77 is_copy: false,
78 }
79 }
80
81 pub fn arity(&self) -> usize {
84 self.relation_desc
85 .as_ref()
86 .map(|desc| desc.typ().column_types.len())
87 .unwrap_or(0)
88 }
89
90 fn with_params(mut self, param_types: Vec<SqlScalarType>) -> Self {
91 self.param_types = param_types;
92 self
93 }
94
95 fn with_is_copy(mut self) -> Self {
96 self.is_copy = true;
97 self
98 }
99}
100
101pub fn describe(
105 pcx: &PlanContext,
106 catalog: &dyn SessionCatalog,
107 stmt: Statement<Aug>,
108 param_types_in: &[Option<SqlScalarType>],
109) -> Result<StatementDesc, PlanError> {
110 let mut param_types = BTreeMap::new();
111 for (i, ty) in param_types_in.iter().enumerate() {
112 if let Some(ty) = ty {
113 param_types.insert(i + 1, ty.clone());
114 }
115 }
116
117 let scx = StatementContext {
118 pcx: Some(pcx),
119 catalog,
120 param_types: RefCell::new(param_types),
121 ambiguous_columns: RefCell::new(false),
122 };
123
124 let desc = match stmt {
125 Statement::AlterCluster(stmt) => ddl::describe_alter_cluster_set_options(&scx, stmt)?,
127 Statement::AlterConnection(stmt) => ddl::describe_alter_connection(&scx, stmt)?,
128 Statement::AlterIndex(stmt) => ddl::describe_alter_index_options(&scx, stmt)?,
129 Statement::AlterObjectRename(stmt) => ddl::describe_alter_object_rename(&scx, stmt)?,
130 Statement::AlterObjectSwap(stmt) => ddl::describe_alter_object_swap(&scx, stmt)?,
131 Statement::AlterRetainHistory(stmt) => ddl::describe_alter_retain_history(&scx, stmt)?,
132 Statement::AlterRole(stmt) => ddl::describe_alter_role(&scx, stmt)?,
133 Statement::AlterSecret(stmt) => ddl::describe_alter_secret_options(&scx, stmt)?,
134 Statement::AlterSetCluster(stmt) => ddl::describe_alter_set_cluster(&scx, stmt)?,
135 Statement::AlterSink(stmt) => ddl::describe_alter_sink(&scx, stmt)?,
136 Statement::AlterSource(stmt) => ddl::describe_alter_source(&scx, stmt)?,
137 Statement::AlterSystemSet(stmt) => ddl::describe_alter_system_set(&scx, stmt)?,
138 Statement::AlterSystemReset(stmt) => ddl::describe_alter_system_reset(&scx, stmt)?,
139 Statement::AlterSystemResetAll(stmt) => ddl::describe_alter_system_reset_all(&scx, stmt)?,
140 Statement::AlterTableAddColumn(stmt) => ddl::describe_alter_table_add_column(&scx, stmt)?,
141 Statement::AlterNetworkPolicy(stmt) => ddl::describe_alter_network_policy(&scx, stmt)?,
142 Statement::Comment(stmt) => ddl::describe_comment(&scx, stmt)?,
143 Statement::CreateCluster(stmt) => ddl::describe_create_cluster(&scx, stmt)?,
144 Statement::CreateClusterReplica(stmt) => ddl::describe_create_cluster_replica(&scx, stmt)?,
145 Statement::CreateConnection(stmt) => ddl::describe_create_connection(&scx, stmt)?,
146 Statement::CreateDatabase(stmt) => ddl::describe_create_database(&scx, stmt)?,
147 Statement::CreateIndex(stmt) => ddl::describe_create_index(&scx, stmt)?,
148 Statement::CreateRole(stmt) => ddl::describe_create_role(&scx, stmt)?,
149 Statement::CreateSchema(stmt) => ddl::describe_create_schema(&scx, stmt)?,
150 Statement::CreateSecret(stmt) => ddl::describe_create_secret(&scx, stmt)?,
151 Statement::CreateSink(stmt) => ddl::describe_create_sink(&scx, stmt)?,
152 Statement::CreateWebhookSource(stmt) => ddl::describe_create_webhook_source(&scx, stmt)?,
153 Statement::CreateSource(stmt) => ddl::describe_create_source(&scx, stmt)?,
154 Statement::CreateSubsource(stmt) => ddl::describe_create_subsource(&scx, stmt)?,
155 Statement::CreateTable(stmt) => ddl::describe_create_table(&scx, stmt)?,
156 Statement::CreateTableFromSource(stmt) => {
157 ddl::describe_create_table_from_source(&scx, stmt)?
158 }
159 Statement::CreateType(stmt) => ddl::describe_create_type(&scx, stmt)?,
160 Statement::CreateView(stmt) => ddl::describe_create_view(&scx, stmt)?,
161 Statement::CreateMaterializedView(stmt) => {
162 ddl::describe_create_materialized_view(&scx, stmt)?
163 }
164 Statement::CreateContinualTask(stmt) => ddl::describe_create_continual_task(&scx, stmt)?,
165 Statement::CreateNetworkPolicy(stmt) => ddl::describe_create_network_policy(&scx, stmt)?,
166 Statement::DropObjects(stmt) => ddl::describe_drop_objects(&scx, stmt)?,
167 Statement::DropOwned(stmt) => ddl::describe_drop_owned(&scx, stmt)?,
168
169 Statement::AlterOwner(stmt) => acl::describe_alter_owner(&scx, stmt)?,
171 Statement::GrantRole(stmt) => acl::describe_grant_role(&scx, stmt)?,
172 Statement::RevokeRole(stmt) => acl::describe_revoke_role(&scx, stmt)?,
173 Statement::GrantPrivileges(stmt) => acl::describe_grant_privileges(&scx, stmt)?,
174 Statement::RevokePrivileges(stmt) => acl::describe_revoke_privileges(&scx, stmt)?,
175 Statement::AlterDefaultPrivileges(stmt) => {
176 acl::describe_alter_default_privileges(&scx, stmt)?
177 }
178 Statement::ReassignOwned(stmt) => acl::describe_reassign_owned(&scx, stmt)?,
179
180 Statement::Show(ShowStatement::ShowColumns(stmt)) => {
182 show::show_columns(&scx, stmt)?.describe()?
183 }
184 Statement::Show(ShowStatement::ShowCreateConnection(stmt)) => {
185 show::describe_show_create_connection(&scx, stmt)?
186 }
187 Statement::Show(ShowStatement::ShowCreateCluster(stmt)) => {
188 show::describe_show_create_cluster(&scx, stmt)?
189 }
190 Statement::Show(ShowStatement::ShowCreateIndex(stmt)) => {
191 show::describe_show_create_index(&scx, stmt)?
192 }
193 Statement::Show(ShowStatement::ShowCreateSink(stmt)) => {
194 show::describe_show_create_sink(&scx, stmt)?
195 }
196 Statement::Show(ShowStatement::ShowCreateSource(stmt)) => {
197 show::describe_show_create_source(&scx, stmt)?
198 }
199 Statement::Show(ShowStatement::ShowCreateTable(stmt)) => {
200 show::describe_show_create_table(&scx, stmt)?
201 }
202 Statement::Show(ShowStatement::ShowCreateView(stmt)) => {
203 show::describe_show_create_view(&scx, stmt)?
204 }
205 Statement::Show(ShowStatement::ShowCreateMaterializedView(stmt)) => {
206 show::describe_show_create_materialized_view(&scx, stmt)?
207 }
208 Statement::Show(ShowStatement::ShowCreateType(stmt)) => {
209 show::describe_show_create_type(&scx, stmt)?
210 }
211 Statement::Show(ShowStatement::ShowObjects(stmt)) => {
212 show::show_objects(&scx, stmt)?.describe()?
213 }
214
215 Statement::Close(stmt) => scl::describe_close(&scx, stmt)?,
217 Statement::Deallocate(stmt) => scl::describe_deallocate(&scx, stmt)?,
218 Statement::Declare(stmt) => scl::describe_declare(&scx, stmt, param_types_in)?,
219 Statement::Discard(stmt) => scl::describe_discard(&scx, stmt)?,
220 Statement::Execute(stmt) => scl::describe_execute(&scx, stmt)?,
221 Statement::Fetch(stmt) => scl::describe_fetch(&scx, stmt)?,
222 Statement::Prepare(stmt) => scl::describe_prepare(&scx, stmt)?,
223 Statement::ResetVariable(stmt) => scl::describe_reset_variable(&scx, stmt)?,
224 Statement::SetVariable(stmt) => scl::describe_set_variable(&scx, stmt)?,
225 Statement::Show(ShowStatement::ShowVariable(stmt)) => {
226 scl::describe_show_variable(&scx, stmt)?
227 }
228
229 Statement::Copy(stmt) => dml::describe_copy(&scx, stmt)?,
231 Statement::Delete(stmt) => dml::describe_delete(&scx, stmt)?,
232 Statement::ExplainPlan(stmt) => dml::describe_explain_plan(&scx, stmt)?,
233 Statement::ExplainPushdown(stmt) => dml::describe_explain_pushdown(&scx, stmt)?,
234 Statement::ExplainAnalyze(stmt) => dml::describe_explain_analyze(&scx, stmt)?,
235 Statement::ExplainTimestamp(stmt) => dml::describe_explain_timestamp(&scx, stmt)?,
236 Statement::ExplainSinkSchema(stmt) => dml::describe_explain_schema(&scx, stmt)?,
237 Statement::Insert(stmt) => dml::describe_insert(&scx, stmt)?,
238 Statement::Select(stmt) => dml::describe_select(&scx, stmt)?,
239 Statement::Subscribe(stmt) => dml::describe_subscribe(&scx, stmt)?,
240 Statement::Update(stmt) => dml::describe_update(&scx, stmt)?,
241
242 Statement::Commit(stmt) => tcl::describe_commit(&scx, stmt)?,
244 Statement::Rollback(stmt) => tcl::describe_rollback(&scx, stmt)?,
245 Statement::SetTransaction(stmt) => tcl::describe_set_transaction(&scx, stmt)?,
246 Statement::StartTransaction(stmt) => tcl::describe_start_transaction(&scx, stmt)?,
247
248 Statement::Raise(stmt) => raise::describe_raise(&scx, stmt)?,
250 Statement::Show(ShowStatement::InspectShard(stmt)) => {
251 scl::describe_inspect_shard(&scx, stmt)?
252 }
253 Statement::ValidateConnection(stmt) => validate::describe_validate_connection(&scx, stmt)?,
254 };
255
256 let desc = desc.with_params(scx.finalize_param_types()?);
257 Ok(desc)
258}
259
260#[mz_ore::instrument(level = "debug")]
277pub fn plan(
278 pcx: Option<&PlanContext>,
279 catalog: &dyn SessionCatalog,
280 stmt: Statement<Aug>,
281 params: &Params,
282 resolved_ids: &ResolvedIds,
283) -> Result<Plan, PlanError> {
284 let param_types = params
285 .expected_types
289 .iter()
290 .enumerate()
291 .map(|(i, ty)| (i + 1, ty.clone()))
292 .collect();
293
294 let kind: StatementKind = (&stmt).into();
295 let permitted_plans = Plan::generated_from(&kind);
296
297 let scx = &mut StatementContext {
298 pcx,
299 catalog,
300 param_types: RefCell::new(param_types),
301 ambiguous_columns: RefCell::new(false),
302 };
303
304 if resolved_ids
305 .items()
306 .filter_map(|id| catalog.try_get_item(id))
308 .any(|item| {
309 item.func().is_ok()
310 && item.name().qualifiers.schema_spec
311 == SchemaSpecifier::Id(catalog.get_mz_unsafe_schema_id())
312 })
313 {
314 scx.require_feature_flag(&vars::UNSAFE_ENABLE_UNSAFE_FUNCTIONS)?;
315 }
316
317 let plan = match stmt {
318 Statement::AlterCluster(stmt) => ddl::plan_alter_cluster(scx, stmt),
320 Statement::AlterConnection(stmt) => ddl::plan_alter_connection(scx, stmt),
321 Statement::AlterIndex(stmt) => ddl::plan_alter_index_options(scx, stmt),
322 Statement::AlterObjectRename(stmt) => ddl::plan_alter_object_rename(scx, stmt),
323 Statement::AlterObjectSwap(stmt) => ddl::plan_alter_object_swap(scx, stmt),
324 Statement::AlterRetainHistory(stmt) => ddl::plan_alter_retain_history(scx, stmt),
325 Statement::AlterRole(stmt) => ddl::plan_alter_role(scx, stmt),
326 Statement::AlterSecret(stmt) => ddl::plan_alter_secret(scx, stmt),
327 Statement::AlterSetCluster(stmt) => ddl::plan_alter_item_set_cluster(scx, stmt),
328 Statement::AlterSink(stmt) => ddl::plan_alter_sink(scx, stmt),
329 Statement::AlterSource(stmt) => ddl::plan_alter_source(scx, stmt),
330 Statement::AlterSystemSet(stmt) => ddl::plan_alter_system_set(scx, stmt),
331 Statement::AlterSystemReset(stmt) => ddl::plan_alter_system_reset(scx, stmt),
332 Statement::AlterSystemResetAll(stmt) => ddl::plan_alter_system_reset_all(scx, stmt),
333 Statement::AlterTableAddColumn(stmt) => ddl::plan_alter_table_add_column(scx, stmt),
334 Statement::AlterNetworkPolicy(stmt) => ddl::plan_alter_network_policy(scx, stmt),
335 Statement::Comment(stmt) => ddl::plan_comment(scx, stmt),
336 Statement::CreateCluster(stmt) => ddl::plan_create_cluster(scx, stmt),
337 Statement::CreateClusterReplica(stmt) => ddl::plan_create_cluster_replica(scx, stmt),
338 Statement::CreateConnection(stmt) => ddl::plan_create_connection(scx, stmt),
339 Statement::CreateDatabase(stmt) => ddl::plan_create_database(scx, stmt),
340 Statement::CreateIndex(stmt) => ddl::plan_create_index(scx, stmt),
341 Statement::CreateRole(stmt) => ddl::plan_create_role(scx, stmt),
342 Statement::CreateSchema(stmt) => ddl::plan_create_schema(scx, stmt),
343 Statement::CreateSecret(stmt) => ddl::plan_create_secret(scx, stmt),
344 Statement::CreateSink(stmt) => ddl::plan_create_sink(scx, stmt),
345 Statement::CreateWebhookSource(stmt) => ddl::plan_create_webhook_source(scx, stmt),
346 Statement::CreateSource(stmt) => ddl::plan_create_source(scx, stmt),
347 Statement::CreateSubsource(stmt) => ddl::plan_create_subsource(scx, stmt),
348 Statement::CreateTable(stmt) => ddl::plan_create_table(scx, stmt),
349 Statement::CreateTableFromSource(stmt) => ddl::plan_create_table_from_source(scx, stmt),
350 Statement::CreateType(stmt) => ddl::plan_create_type(scx, stmt),
351 Statement::CreateView(stmt) => ddl::plan_create_view(scx, stmt),
352 Statement::CreateMaterializedView(stmt) => ddl::plan_create_materialized_view(scx, stmt),
353 Statement::CreateContinualTask(stmt) => ddl::plan_create_continual_task(scx, stmt),
354 Statement::CreateNetworkPolicy(stmt) => ddl::plan_create_network_policy(scx, stmt),
355 Statement::DropObjects(stmt) => ddl::plan_drop_objects(scx, stmt),
356 Statement::DropOwned(stmt) => ddl::plan_drop_owned(scx, stmt),
357
358 Statement::AlterOwner(stmt) => acl::plan_alter_owner(scx, stmt),
360 Statement::GrantRole(stmt) => acl::plan_grant_role(scx, stmt),
361 Statement::RevokeRole(stmt) => acl::plan_revoke_role(scx, stmt),
362 Statement::GrantPrivileges(stmt) => acl::plan_grant_privileges(scx, stmt),
363 Statement::RevokePrivileges(stmt) => acl::plan_revoke_privileges(scx, stmt),
364 Statement::AlterDefaultPrivileges(stmt) => acl::plan_alter_default_privileges(scx, stmt),
365 Statement::ReassignOwned(stmt) => acl::plan_reassign_owned(scx, stmt),
366
367 Statement::Copy(stmt) => dml::plan_copy(scx, stmt),
369 Statement::Delete(stmt) => dml::plan_delete(scx, stmt, params),
370 Statement::ExplainPlan(stmt) => dml::plan_explain_plan(scx, stmt, params),
371 Statement::ExplainPushdown(stmt) => dml::plan_explain_pushdown(scx, stmt, params),
372 Statement::ExplainAnalyze(stmt) => dml::plan_explain_analyze(scx, stmt, params),
373 Statement::ExplainTimestamp(stmt) => dml::plan_explain_timestamp(scx, stmt),
374 Statement::ExplainSinkSchema(stmt) => dml::plan_explain_schema(scx, stmt),
375 Statement::Insert(stmt) => dml::plan_insert(scx, stmt, params),
376 Statement::Select(stmt) => dml::plan_select(scx, stmt, params, None),
377 Statement::Subscribe(stmt) => dml::plan_subscribe(scx, stmt, params, None),
378 Statement::Update(stmt) => dml::plan_update(scx, stmt, params),
379
380 Statement::Show(ShowStatement::ShowColumns(stmt)) => show::show_columns(scx, stmt)?.plan(),
382 Statement::Show(ShowStatement::ShowCreateConnection(stmt)) => {
383 show::plan_show_create_connection(scx, stmt).map(Plan::ShowCreate)
384 }
385 Statement::Show(ShowStatement::ShowCreateCluster(stmt)) => {
386 show::plan_show_create_cluster(scx, stmt).map(Plan::ShowCreate)
387 }
388 Statement::Show(ShowStatement::ShowCreateIndex(stmt)) => {
389 show::plan_show_create_index(scx, stmt).map(Plan::ShowCreate)
390 }
391 Statement::Show(ShowStatement::ShowCreateSink(stmt)) => {
392 show::plan_show_create_sink(scx, stmt).map(Plan::ShowCreate)
393 }
394 Statement::Show(ShowStatement::ShowCreateSource(stmt)) => {
395 show::plan_show_create_source(scx, stmt).map(Plan::ShowCreate)
396 }
397 Statement::Show(ShowStatement::ShowCreateTable(stmt)) => {
398 show::plan_show_create_table(scx, stmt).map(Plan::ShowCreate)
399 }
400 Statement::Show(ShowStatement::ShowCreateView(stmt)) => {
401 show::plan_show_create_view(scx, stmt).map(Plan::ShowCreate)
402 }
403 Statement::Show(ShowStatement::ShowCreateMaterializedView(stmt)) => {
404 show::plan_show_create_materialized_view(scx, stmt).map(Plan::ShowCreate)
405 }
406 Statement::Show(ShowStatement::ShowCreateType(stmt)) => {
407 show::plan_show_create_type(scx, stmt).map(Plan::ShowCreate)
408 }
409 Statement::Show(ShowStatement::ShowObjects(stmt)) => show::show_objects(scx, stmt)?.plan(),
410
411 Statement::Close(stmt) => scl::plan_close(scx, stmt),
413 Statement::Deallocate(stmt) => scl::plan_deallocate(scx, stmt),
414 Statement::Declare(stmt) => scl::plan_declare(scx, stmt, params),
415 Statement::Discard(stmt) => scl::plan_discard(scx, stmt),
416 Statement::Execute(stmt) => scl::plan_execute(scx, stmt),
417 Statement::Fetch(stmt) => scl::plan_fetch(scx, stmt),
418 Statement::Prepare(stmt) => scl::plan_prepare(scx, stmt),
419 Statement::ResetVariable(stmt) => scl::plan_reset_variable(scx, stmt),
420 Statement::SetVariable(stmt) => scl::plan_set_variable(scx, stmt),
421 Statement::Show(ShowStatement::ShowVariable(stmt)) => scl::plan_show_variable(scx, stmt),
422
423 Statement::Commit(stmt) => tcl::plan_commit(scx, stmt),
425 Statement::Rollback(stmt) => tcl::plan_rollback(scx, stmt),
426 Statement::SetTransaction(stmt) => tcl::plan_set_transaction(scx, stmt),
427 Statement::StartTransaction(stmt) => tcl::plan_start_transaction(scx, stmt),
428
429 Statement::Raise(stmt) => raise::plan_raise(scx, stmt),
431 Statement::Show(ShowStatement::InspectShard(stmt)) => scl::plan_inspect_shard(scx, stmt),
432 Statement::ValidateConnection(stmt) => validate::plan_validate_connection(scx, stmt),
433 };
434
435 if let Ok(plan) = &plan {
436 mz_ore::soft_assert_no_log!(
437 permitted_plans.contains(&PlanKind::from(plan)),
438 "plan {:?}, permitted plans {:?}",
439 plan,
440 permitted_plans
441 );
442 }
443
444 plan
445}
446
447pub fn plan_copy_from(
448 pcx: &PlanContext,
449 catalog: &dyn SessionCatalog,
450 target_id: CatalogItemId,
451 target_name: String,
452 columns: Vec<ColumnIndex>,
453 rows: Vec<mz_repr::Row>,
454) -> Result<super::HirRelationExpr, PlanError> {
455 query::plan_copy_from_rows(pcx, catalog, target_id, target_name, columns, rows)
456}
457
458impl PartialEq<ObjectType> for CatalogItemType {
465 fn eq(&self, other: &ObjectType) -> bool {
466 match (self, other) {
467 (CatalogItemType::Source, ObjectType::Source)
468 | (CatalogItemType::Table, ObjectType::Table)
469 | (CatalogItemType::Sink, ObjectType::Sink)
470 | (CatalogItemType::View, ObjectType::View)
471 | (CatalogItemType::MaterializedView, ObjectType::MaterializedView)
472 | (CatalogItemType::Index, ObjectType::Index)
473 | (CatalogItemType::Type, ObjectType::Type)
474 | (CatalogItemType::Secret, ObjectType::Secret)
475 | (CatalogItemType::Connection, ObjectType::Connection) => true,
476 (_, _) => false,
477 }
478 }
479}
480
481impl PartialEq<CatalogItemType> for ObjectType {
482 fn eq(&self, other: &CatalogItemType) -> bool {
483 other == self
484 }
485}
486
487#[derive(Debug, Clone)]
489pub struct StatementContext<'a> {
490 pcx: Option<&'a PlanContext>,
495 pub catalog: &'a dyn SessionCatalog,
496 pub param_types: RefCell<BTreeMap<usize, SqlScalarType>>,
499 pub ambiguous_columns: RefCell<bool>,
502}
503
504impl<'a> StatementContext<'a> {
505 pub fn new(
506 pcx: Option<&'a PlanContext>,
507 catalog: &'a dyn SessionCatalog,
508 ) -> StatementContext<'a> {
509 StatementContext {
510 pcx,
511 catalog,
512 param_types: Default::default(),
513 ambiguous_columns: RefCell::new(false),
514 }
515 }
516
517 pub fn current_schemas(&self) -> &[(ResolvedDatabaseSpecifier, SchemaSpecifier)] {
519 self.catalog.search_path()
520 }
521
522 pub fn current_schema(&self) -> Option<&(ResolvedDatabaseSpecifier, SchemaSpecifier)> {
525 self.current_schemas().into_iter().next()
526 }
527
528 pub fn pcx(&self) -> Result<&PlanContext, PlanError> {
529 self.pcx.ok_or_else(|| sql_err!("no plan context"))
530 }
531
532 pub fn allocate_full_name(&self, name: PartialItemName) -> Result<FullItemName, PlanError> {
533 let (database, schema): (RawDatabaseSpecifier, String) = match (name.database, name.schema)
534 {
535 (None, None) => {
536 let Some((database, schema)) = self.current_schema() else {
537 return Err(PlanError::InvalidSchemaName);
538 };
539 let schema = self.get_schema(database, schema);
540 let database = match schema.database() {
541 ResolvedDatabaseSpecifier::Ambient => RawDatabaseSpecifier::Ambient,
542 ResolvedDatabaseSpecifier::Id(id) => {
543 RawDatabaseSpecifier::Name(self.catalog.get_database(id).name().to_string())
544 }
545 };
546 (database, schema.name().schema.clone())
547 }
548 (None, Some(schema)) => {
549 if is_system_schema(&schema) {
550 (RawDatabaseSpecifier::Ambient, schema)
551 } else {
552 match self.catalog.active_database_name() {
553 Some(name) => (RawDatabaseSpecifier::Name(name.to_string()), schema),
554 None => {
555 sql_bail!(
556 "no database specified for non-system schema and no active database"
557 )
558 }
559 }
560 }
561 }
562 (Some(_database), None) => {
563 sql_bail!("unreachable: specified the database but no schema")
566 }
567 (Some(database), Some(schema)) => (RawDatabaseSpecifier::Name(database), schema),
568 };
569 let item = name.item;
570 Ok(FullItemName {
571 database,
572 schema,
573 item,
574 })
575 }
576
577 pub fn allocate_qualified_name(
578 &self,
579 name: PartialItemName,
580 ) -> Result<QualifiedItemName, PlanError> {
581 let full_name = self.allocate_full_name(name)?;
582 let database_spec = match full_name.database {
583 RawDatabaseSpecifier::Ambient => ResolvedDatabaseSpecifier::Ambient,
584 RawDatabaseSpecifier::Name(name) => ResolvedDatabaseSpecifier::Id(
585 self.resolve_database(&UnresolvedDatabaseName(Ident::new(name)?))?
586 .id(),
587 ),
588 };
589 let schema_spec = self
590 .resolve_schema_in_database(&database_spec, &Ident::new(full_name.schema)?)?
591 .id()
592 .clone();
593 Ok(QualifiedItemName {
594 qualifiers: ItemQualifiers {
595 database_spec,
596 schema_spec,
597 },
598 item: full_name.item,
599 })
600 }
601
602 pub fn allocate_temporary_full_name(&self, name: PartialItemName) -> FullItemName {
603 FullItemName {
604 database: RawDatabaseSpecifier::Ambient,
605 schema: name
606 .schema
607 .unwrap_or_else(|| mz_repr::namespaces::MZ_TEMP_SCHEMA.to_owned()),
608 item: name.item,
609 }
610 }
611
612 pub fn allocate_temporary_qualified_name(
613 &self,
614 name: PartialItemName,
615 ) -> Result<QualifiedItemName, PlanError> {
616 if let Some(name) = name.schema {
617 if name
618 != self
619 .get_schema(
620 &ResolvedDatabaseSpecifier::Ambient,
621 &SchemaSpecifier::Temporary,
622 )
623 .name()
624 .schema
625 {
626 return Err(PlanError::InvalidTemporarySchema);
627 }
628 }
629
630 Ok(QualifiedItemName {
631 qualifiers: ItemQualifiers {
632 database_spec: ResolvedDatabaseSpecifier::Ambient,
633 schema_spec: SchemaSpecifier::Temporary,
634 },
635 item: name.item,
636 })
637 }
638
639 pub fn allocate_resolved_item_name(
642 &self,
643 id: CatalogItemId,
644 name: UnresolvedItemName,
645 ) -> Result<ResolvedItemName, PlanError> {
646 let partial = normalize::unresolved_item_name(name)?;
647 let qualified = self.allocate_qualified_name(partial.clone())?;
648 let full_name = self.allocate_full_name(partial)?;
649 Ok(ResolvedItemName::Item {
650 id,
651 qualifiers: qualified.qualifiers,
652 full_name,
653 print_id: true,
654 version: RelationVersionSelector::Latest,
655 })
656 }
657
658 pub fn active_database(&self) -> Option<&DatabaseId> {
659 self.catalog.active_database()
660 }
661
662 pub fn resolve_optional_schema(
663 &self,
664 schema_name: &Option<ResolvedSchemaName>,
665 ) -> Result<SchemaSpecifier, PlanError> {
666 match schema_name {
667 Some(ResolvedSchemaName::Schema { schema_spec, .. }) => Ok(schema_spec.clone()),
668 None => self.resolve_active_schema().map(|spec| spec.clone()),
669 Some(ResolvedSchemaName::Error) => {
670 unreachable!("should have been handled by name resolution")
671 }
672 }
673 }
674
675 pub fn resolve_active_schema(&self) -> Result<&SchemaSpecifier, PlanError> {
676 match self.current_schema() {
677 Some((_db, schema)) => Ok(schema),
678 None => Err(PlanError::InvalidSchemaName),
679 }
680 }
681
682 pub fn get_cluster(&self, id: &ClusterId) -> &dyn CatalogCluster<'_> {
683 self.catalog.get_cluster(*id)
684 }
685
686 pub fn resolve_database(
687 &self,
688 name: &UnresolvedDatabaseName,
689 ) -> Result<&dyn CatalogDatabase, PlanError> {
690 let name = normalize::ident_ref(&name.0);
691 Ok(self.catalog.resolve_database(name)?)
692 }
693
694 pub fn get_database(&self, id: &DatabaseId) -> &dyn CatalogDatabase {
695 self.catalog.get_database(id)
696 }
697
698 pub fn resolve_schema_in_database(
699 &self,
700 database_spec: &ResolvedDatabaseSpecifier,
701 schema: &Ident,
702 ) -> Result<&dyn CatalogSchema, PlanError> {
703 let schema = normalize::ident_ref(schema);
704 Ok(self
705 .catalog
706 .resolve_schema_in_database(database_spec, schema)?)
707 }
708
709 pub fn resolve_schema(
710 &self,
711 name: UnresolvedSchemaName,
712 ) -> Result<&dyn CatalogSchema, PlanError> {
713 let name = normalize::unresolved_schema_name(name)?;
714 Ok(self
715 .catalog
716 .resolve_schema(name.database.as_deref(), &name.schema)?)
717 }
718
719 pub fn get_schema(
720 &self,
721 database_spec: &ResolvedDatabaseSpecifier,
722 schema_spec: &SchemaSpecifier,
723 ) -> &dyn CatalogSchema {
724 self.catalog.get_schema(database_spec, schema_spec)
725 }
726
727 pub fn resolve_item(&self, name: RawItemName) -> Result<&dyn CatalogItem, PlanError> {
728 match name {
729 RawItemName::Name(name) => {
730 let name = normalize::unresolved_item_name(name)?;
731 Ok(self.catalog.resolve_item(&name)?)
732 }
733 RawItemName::Id(id, _, _) => {
734 let gid = id.parse()?;
735 Ok(self.catalog.get_item(&gid))
736 }
737 }
738 }
739
740 pub fn get_item(&self, id: &CatalogItemId) -> &dyn CatalogItem {
741 self.catalog.get_item(id)
742 }
743
744 pub fn get_item_by_resolved_name(
745 &self,
746 name: &ResolvedItemName,
747 ) -> Result<Box<dyn CatalogCollectionItem + '_>, PlanError> {
748 match name {
749 ResolvedItemName::Item { id, version, .. } => {
750 Ok(self.get_item(id).at_version(*version))
751 }
752 ResolvedItemName::Cte { .. } => sql_bail!("non-user item"),
753 ResolvedItemName::ContinualTask { .. } => sql_bail!("non-user item"),
754 ResolvedItemName::Error => unreachable!("should have been caught in name resolution"),
755 }
756 }
757
758 pub fn get_column_by_resolved_name(
759 &self,
760 name: &ColumnName<Aug>,
761 ) -> Result<(Box<dyn CatalogCollectionItem + '_>, usize), PlanError> {
762 match (&name.relation, &name.column) {
763 (
764 ResolvedItemName::Item { id, version, .. },
765 ResolvedColumnReference::Column { index, .. },
766 ) => {
767 let item = self.get_item(id).at_version(*version);
768 Ok((item, *index))
769 }
770 _ => unreachable!(
771 "get_column_by_resolved_name errors should have been caught in name resolution"
772 ),
773 }
774 }
775
776 pub fn resolve_function(
777 &self,
778 name: UnresolvedItemName,
779 ) -> Result<&dyn CatalogItem, PlanError> {
780 let name = normalize::unresolved_item_name(name)?;
781 Ok(self.catalog.resolve_function(&name)?)
782 }
783
784 pub fn resolve_cluster(
785 &self,
786 name: Option<&Ident>,
787 ) -> Result<&dyn CatalogCluster<'_>, PlanError> {
788 let name = name.map(|name| name.as_str());
789 Ok(self.catalog.resolve_cluster(name)?)
790 }
791
792 pub fn resolve_type(&self, mut ty: mz_pgrepr::Type) -> Result<ResolvedDataType, PlanError> {
793 match &mut ty {
799 mz_pgrepr::Type::Interval { constraints } => *constraints = None,
800 mz_pgrepr::Type::Time { precision } => *precision = None,
801 mz_pgrepr::Type::TimeTz { precision } => *precision = None,
802 mz_pgrepr::Type::Timestamp { precision } => *precision = None,
803 mz_pgrepr::Type::TimestampTz { precision } => *precision = None,
804 _ => (),
805 }
806 let mut ty = if ty.oid() >= FIRST_USER_OID {
812 sql_bail!("internal error, unexpected user type: {ty:?} ");
813 } else if ty.oid() < FIRST_MATERIALIZE_OID {
814 format!("pg_catalog.{}", ty)
815 } else {
816 format!("mz_catalog.{}", ty)
818 };
819 if ty == "pg_catalog.json" {
823 ty = "pg_catalog.jsonb".into();
824 }
825 let data_type = mz_sql_parser::parser::parse_data_type(&ty)?;
826 let (data_type, _) = names::resolve(self.catalog, data_type)?;
827 Ok(data_type)
828 }
829
830 pub fn get_object_type(&self, id: &ObjectId) -> ObjectType {
831 self.catalog.get_object_type(id)
832 }
833
834 pub fn get_system_object_type(&self, id: &SystemObjectId) -> SystemObjectType {
835 match id {
836 SystemObjectId::Object(id) => SystemObjectType::Object(self.get_object_type(id)),
837 SystemObjectId::System => SystemObjectType::System,
838 }
839 }
840
841 pub fn require_feature_flag(&self, flag: &'static FeatureFlag) -> Result<(), PlanError> {
843 flag.require(self.catalog.system_vars())?;
844 Ok(())
845 }
846
847 pub fn is_feature_flag_enabled(&self, flag: &'static FeatureFlag) -> bool {
849 self.require_feature_flag(flag).is_ok()
850 }
851
852 pub fn finalize_param_types(self) -> Result<Vec<SqlScalarType>, PlanError> {
853 let param_types = self.param_types.into_inner();
854 let mut out = vec![];
855 for (i, (n, typ)) in param_types.into_iter().enumerate() {
856 if n != i + 1 {
857 sql_bail!("unable to infer type for parameter ${}", i + 1);
858 }
859 out.push(typ);
860 }
861 Ok(out)
862 }
863
864 pub fn humanize_scalar_type(&self, typ: &SqlScalarType, postgres_compat: bool) -> String {
867 self.catalog.humanize_scalar_type(typ, postgres_compat)
868 }
869
870 pub fn humanize_column_type(&self, typ: &SqlColumnType, postgres_compat: bool) -> String {
873 self.catalog.humanize_column_type(typ, postgres_compat)
874 }
875
876 pub(crate) fn build_tunnel_definition(
877 &self,
878 ssh_tunnel: Option<with_options::Object>,
879 aws_privatelink: Option<ConnectionDefaultAwsPrivatelink<Aug>>,
880 ) -> Result<Tunnel<ReferencedConnection>, PlanError> {
881 match (ssh_tunnel, aws_privatelink) {
882 (None, None) => Ok(Tunnel::Direct),
883 (Some(ssh_tunnel), None) => {
884 let id = CatalogItemId::from(ssh_tunnel);
885 let ssh_tunnel = self.catalog.get_item(&id);
886 match ssh_tunnel.connection()? {
887 Connection::Ssh(_connection) => Ok(Tunnel::Ssh(SshTunnel {
888 connection_id: id,
889 connection: id,
890 })),
891 _ => sql_bail!("{} is not an SSH connection", ssh_tunnel.name().item),
892 }
893 }
894 (None, Some(aws_privatelink)) => {
895 let id = aws_privatelink.connection.item_id().clone();
896 let entry = self.catalog.get_item(&id);
897 match entry.connection()? {
898 Connection::AwsPrivatelink(_) => Ok(Tunnel::AwsPrivatelink(AwsPrivatelink {
899 connection_id: id,
900 availability_zone: None,
902 port: aws_privatelink.port,
904 })),
905 _ => sql_bail!("{} is not an AWS PRIVATELINK connection", entry.name().item),
906 }
907 }
908 (Some(_), Some(_)) => {
909 sql_bail!("cannot specify both SSH TUNNEL and AWS PRIVATELINK");
910 }
911 }
912 }
913
914 pub fn relation_desc_into_table_defs(
915 &self,
916 desc: &RelationDesc,
917 ) -> Result<(Vec<ColumnDef<Aug>>, Vec<TableConstraint<Aug>>), PlanError> {
918 let mut columns = vec![];
919 let mut null_cols = BTreeSet::new();
920 for (column_name, column_type) in desc.iter() {
921 let name = Ident::new(column_name.as_str().to_owned())?;
922
923 let ty = mz_pgrepr::Type::from(&column_type.scalar_type);
924 let data_type = self.resolve_type(ty)?;
925
926 let options = if !column_type.nullable {
927 null_cols.insert(columns.len());
928 vec![mz_sql_parser::ast::ColumnOptionDef {
929 name: None,
930 option: mz_sql_parser::ast::ColumnOption::NotNull,
931 }]
932 } else {
933 vec![]
934 };
935
936 columns.push(ColumnDef {
937 name,
938 data_type,
939 collation: None,
940 options,
941 });
942 }
943
944 let mut table_constraints = vec![];
945 for key in desc.typ().keys.iter() {
946 let mut col_names = vec![];
947 for col_idx in key {
948 if !null_cols.contains(col_idx) {
949 sql_bail!(
952 "[internal error] key columns must be NOT NULL when generating table constraints"
953 );
954 }
955 col_names.push(columns[*col_idx].name.clone());
956 }
957 table_constraints.push(TableConstraint::Unique {
958 name: None,
959 columns: col_names,
960 is_primary: false,
961 nulls_not_distinct: false,
962 });
963 }
964
965 Ok((columns, table_constraints))
966 }
967
968 pub fn get_owner_id(&self, id: &ObjectId) -> Option<RoleId> {
969 self.catalog.get_owner_id(id)
970 }
971
972 pub fn humanize_resolved_name(
973 &self,
974 name: &ResolvedItemName,
975 ) -> Result<PartialItemName, PlanError> {
976 let item = self.get_item_by_resolved_name(name)?;
977 Ok(self.catalog.minimal_qualification(item.name()))
978 }
979
980 pub fn dangerous_resolve_name(&self, name: Vec<&str>) -> ResolvedItemName {
983 tracing::trace!("dangerous_resolve_name {:?}", name);
984 let name: Vec<_> = name.into_iter().map(Ident::new_unchecked).collect();
986 let name = UnresolvedItemName::qualified(&name);
987 let entry = match self.resolve_item(RawItemName::Name(name.clone())) {
988 Ok(entry) => entry,
989 Err(_) => self
990 .resolve_function(name.clone())
991 .expect("name referred to an existing object"),
992 };
993
994 let partial = normalize::unresolved_item_name(name).unwrap();
995 let full_name = self.allocate_full_name(partial).unwrap();
996
997 ResolvedItemName::Item {
998 id: entry.id(),
999 qualifiers: entry.name().qualifiers.clone(),
1000 full_name,
1001 print_id: true,
1002 version: RelationVersionSelector::Latest,
1003 }
1004 }
1005}
1006
1007pub fn resolve_cluster_for_materialized_view<'a>(
1008 catalog: &'a dyn SessionCatalog,
1009 stmt: &CreateMaterializedViewStatement<Aug>,
1010) -> Result<ClusterId, PlanError> {
1011 Ok(match &stmt.in_cluster {
1012 None => catalog.resolve_cluster(None)?.id(),
1013 Some(in_cluster) => in_cluster.id,
1014 })
1015}
1016
1017#[derive(Debug, Clone, Copy)]
1019pub enum StatementClassification {
1020 ACL,
1021 DDL,
1022 DML,
1023 Other,
1024 SCL,
1025 Show,
1026 TCL,
1027}
1028
1029impl StatementClassification {
1030 pub fn is_ddl(&self) -> bool {
1031 matches!(self, StatementClassification::DDL)
1032 }
1033}
1034
1035impl<T: mz_sql_parser::ast::AstInfo> From<&Statement<T>> for StatementClassification {
1036 fn from(value: &Statement<T>) -> Self {
1037 use StatementClassification::*;
1038
1039 match value {
1040 Statement::AlterCluster(_) => DDL,
1042 Statement::AlterConnection(_) => DDL,
1043 Statement::AlterIndex(_) => DDL,
1044 Statement::AlterObjectRename(_) => DDL,
1045 Statement::AlterObjectSwap(_) => DDL,
1046 Statement::AlterNetworkPolicy(_) => DDL,
1047 Statement::AlterRetainHistory(_) => DDL,
1048 Statement::AlterRole(_) => DDL,
1049 Statement::AlterSecret(_) => DDL,
1050 Statement::AlterSetCluster(_) => DDL,
1051 Statement::AlterSink(_) => DDL,
1052 Statement::AlterSource(_) => DDL,
1053 Statement::AlterSystemSet(_) => DDL,
1054 Statement::AlterSystemReset(_) => DDL,
1055 Statement::AlterSystemResetAll(_) => DDL,
1056 Statement::AlterTableAddColumn(_) => DDL,
1057 Statement::Comment(_) => DDL,
1058 Statement::CreateCluster(_) => DDL,
1059 Statement::CreateClusterReplica(_) => DDL,
1060 Statement::CreateConnection(_) => DDL,
1061 Statement::CreateContinualTask(_) => DDL,
1062 Statement::CreateDatabase(_) => DDL,
1063 Statement::CreateIndex(_) => DDL,
1064 Statement::CreateRole(_) => DDL,
1065 Statement::CreateSchema(_) => DDL,
1066 Statement::CreateSecret(_) => DDL,
1067 Statement::CreateSink(_) => DDL,
1068 Statement::CreateWebhookSource(_) => DDL,
1069 Statement::CreateSource(_) => DDL,
1070 Statement::CreateSubsource(_) => DDL,
1071 Statement::CreateTable(_) => DDL,
1072 Statement::CreateTableFromSource(_) => DDL,
1073 Statement::CreateType(_) => DDL,
1074 Statement::CreateView(_) => DDL,
1075 Statement::CreateMaterializedView(_) => DDL,
1076 Statement::CreateNetworkPolicy(_) => DDL,
1077 Statement::DropObjects(_) => DDL,
1078 Statement::DropOwned(_) => DDL,
1079
1080 Statement::AlterOwner(_) => ACL,
1082 Statement::GrantRole(_) => ACL,
1083 Statement::RevokeRole(_) => ACL,
1084 Statement::GrantPrivileges(_) => ACL,
1085 Statement::RevokePrivileges(_) => ACL,
1086 Statement::AlterDefaultPrivileges(_) => ACL,
1087 Statement::ReassignOwned(_) => ACL,
1088
1089 Statement::Copy(_) => DML,
1091 Statement::Delete(_) => DML,
1092 Statement::ExplainPlan(_) => DML,
1093 Statement::ExplainPushdown(_) => DML,
1094 Statement::ExplainAnalyze(_) => DML,
1095 Statement::ExplainTimestamp(_) => DML,
1096 Statement::ExplainSinkSchema(_) => DML,
1097 Statement::Insert(_) => DML,
1098 Statement::Select(_) => DML,
1099 Statement::Subscribe(_) => DML,
1100 Statement::Update(_) => DML,
1101
1102 Statement::Show(ShowStatement::ShowColumns(_)) => Show,
1104 Statement::Show(ShowStatement::ShowCreateConnection(_)) => Show,
1105 Statement::Show(ShowStatement::ShowCreateCluster(_)) => Show,
1106 Statement::Show(ShowStatement::ShowCreateIndex(_)) => Show,
1107 Statement::Show(ShowStatement::ShowCreateSink(_)) => Show,
1108 Statement::Show(ShowStatement::ShowCreateSource(_)) => Show,
1109 Statement::Show(ShowStatement::ShowCreateTable(_)) => Show,
1110 Statement::Show(ShowStatement::ShowCreateView(_)) => Show,
1111 Statement::Show(ShowStatement::ShowCreateMaterializedView(_)) => Show,
1112 Statement::Show(ShowStatement::ShowCreateType(_)) => Show,
1113 Statement::Show(ShowStatement::ShowObjects(_)) => Show,
1114
1115 Statement::Close(_) => SCL,
1117 Statement::Deallocate(_) => SCL,
1118 Statement::Declare(_) => SCL,
1119 Statement::Discard(_) => SCL,
1120 Statement::Execute(_) => SCL,
1121 Statement::Fetch(_) => SCL,
1122 Statement::Prepare(_) => SCL,
1123 Statement::ResetVariable(_) => SCL,
1124 Statement::SetVariable(_) => SCL,
1125 Statement::Show(ShowStatement::ShowVariable(_)) => SCL,
1126
1127 Statement::Commit(_) => TCL,
1129 Statement::Rollback(_) => TCL,
1130 Statement::SetTransaction(_) => TCL,
1131 Statement::StartTransaction(_) => TCL,
1132
1133 Statement::Raise(_) => Other,
1135 Statement::Show(ShowStatement::InspectShard(_)) => Other,
1136 Statement::ValidateConnection(_) => Other,
1137 }
1138 }
1139}