1use std::fmt;
18
19use itertools::Itertools;
20use mz_repr::{ColumnName, GlobalId};
21use mz_sql_parser::ast::display::AstDisplay;
22use mz_sql_parser::ast::visit_mut::{self, VisitMut};
23use mz_sql_parser::ast::{
24 CreateConnectionStatement, CreateIndexStatement, CreateMaterializedViewStatement,
25 CreateSecretStatement, CreateSinkStatement, CreateSourceStatement, CreateSubsourceStatement,
26 CreateTableFromSourceStatement, CreateTableStatement, CreateTypeStatement, CreateViewStatement,
27 CreateWebhookSourceStatement, CteBlock, Function, FunctionArgs, Ident, IfExistsBehavior,
28 MutRecBlock, Op, Query, Statement, TableFactor, TableFromSourceColumns, UnresolvedItemName,
29 UnresolvedSchemaName, Value, ViewDefinition,
30};
31
32use crate::names::{Aug, FullItemName, PartialItemName, PartialSchemaName, RawDatabaseSpecifier};
33use crate::plan::error::PlanError;
34use crate::plan::statement::StatementContext;
35
36pub fn ident(ident: Ident) -> String {
38 ident.into_string()
39}
40
41pub fn ident_ref(ident: &Ident) -> &str {
43 ident.as_str()
44}
45
46pub fn column_name(id: Ident) -> ColumnName {
48 ColumnName::from(ident(id))
49}
50
51pub fn unresolved_item_name(mut name: UnresolvedItemName) -> Result<PartialItemName, PlanError> {
53 if name.0.len() < 1 || name.0.len() > 3 {
54 return Err(PlanError::MisqualifiedName(name.to_string()));
55 }
56 let out = PartialItemName {
57 item: ident(
58 name.0
59 .pop()
60 .expect("name checked to have at least one component"),
61 ),
62 schema: name.0.pop().map(ident),
63 database: name.0.pop().map(ident),
64 };
65 assert!(name.0.is_empty());
66 Ok(out)
67}
68
69pub fn unresolved_schema_name(
71 mut name: UnresolvedSchemaName,
72) -> Result<PartialSchemaName, PlanError> {
73 if name.0.len() < 1 || name.0.len() > 2 {
74 return Err(PlanError::MisqualifiedName(name.to_string()));
75 }
76 let out = PartialSchemaName {
77 schema: ident(
78 name.0
79 .pop()
80 .expect("name checked to have at least one component"),
81 ),
82 database: name.0.pop().map(ident),
83 };
84 assert!(name.0.is_empty());
85 Ok(out)
86}
87
88pub fn op(op: &Op) -> Result<&str, PlanError> {
92 if let Some(namespace) = &op.namespace {
93 if namespace.len() != 0
94 && (namespace.len() != 1
95 || namespace[0].as_str() != mz_repr::namespaces::PG_CATALOG_SCHEMA)
96 {
97 sql_bail!(
98 "operator does not exist: {}.{}",
99 namespace.iter().map(|n| n.to_string()).join("."),
100 op.op,
101 )
102 }
103 }
104 Ok(&op.op)
105}
106
107#[derive(Debug, Clone)]
108pub enum SqlValueOrSecret {
109 Value(Value),
110 Secret(GlobalId),
111}
112
113impl fmt::Display for SqlValueOrSecret {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self {
116 SqlValueOrSecret::Value(v) => write!(f, "{}", v),
117 SqlValueOrSecret::Secret(id) => write!(f, "{}", id),
118 }
119 }
120}
121
122impl From<SqlValueOrSecret> for Option<Value> {
123 fn from(s: SqlValueOrSecret) -> Self {
124 match s {
125 SqlValueOrSecret::Value(v) => Some(v),
126 SqlValueOrSecret::Secret(_id) => None,
127 }
128 }
129}
130
131pub fn unresolve(name: FullItemName) -> UnresolvedItemName {
135 let mut out = vec![];
137 if let RawDatabaseSpecifier::Name(n) = name.database {
138 out.push(Ident::new_unchecked(n));
139 }
140 out.push(Ident::new_unchecked(name.schema));
141 out.push(Ident::new_unchecked(name.item));
142 UnresolvedItemName(out)
143}
144
145pub fn full_name(mut raw_name: UnresolvedItemName) -> Result<FullItemName, PlanError> {
148 match raw_name.0.len() {
149 3 => Ok(FullItemName {
150 item: ident(raw_name.0.pop().unwrap()),
151 schema: ident(raw_name.0.pop().unwrap()),
152 database: RawDatabaseSpecifier::Name(ident(raw_name.0.pop().unwrap())),
153 }),
154 2 => Ok(FullItemName {
155 item: ident(raw_name.0.pop().unwrap()),
156 schema: ident(raw_name.0.pop().unwrap()),
157 database: RawDatabaseSpecifier::Ambient,
158 }),
159 _ => sql_bail!("unresolved name {} not fully qualified", raw_name),
160 }
161}
162
163pub fn create_statement(
172 scx: &StatementContext,
173 mut stmt: Statement<Aug>,
174) -> Result<String, PlanError> {
175 let allocate_name = |name: &UnresolvedItemName| -> Result<_, PlanError> {
176 Ok(unresolve(
177 scx.allocate_full_name(unresolved_item_name(name.clone())?)?,
178 ))
179 };
180
181 let allocate_temporary_name = |name: &UnresolvedItemName| -> Result<_, PlanError> {
182 Ok(unresolve(scx.allocate_temporary_full_name(
183 unresolved_item_name(name.clone())?,
184 )))
185 };
186
187 struct QueryNormalizer {
188 ctes: Vec<Ident>,
189 err: Option<PlanError>,
190 }
191
192 impl QueryNormalizer {
193 fn new() -> QueryNormalizer {
194 QueryNormalizer {
195 ctes: vec![],
196 err: None,
197 }
198 }
199 }
200
201 impl<'ast> VisitMut<'ast, Aug> for QueryNormalizer {
202 fn visit_query_mut(&mut self, query: &'ast mut Query<Aug>) {
203 let n = self.ctes.len();
204 match &query.ctes {
205 CteBlock::Simple(ctes) => {
206 for cte in ctes.iter() {
207 self.ctes.push(cte.alias.name.clone());
208 }
209 }
210 CteBlock::MutuallyRecursive(MutRecBlock { options: _, ctes }) => {
211 for cte in ctes.iter() {
212 self.ctes.push(cte.name.clone());
213 }
214 }
215 }
216 visit_mut::visit_query_mut(self, query);
217 self.ctes.truncate(n);
218 }
219
220 fn visit_function_mut(&mut self, func: &'ast mut Function<Aug>) {
221 match &mut func.args {
222 FunctionArgs::Star => (),
223 FunctionArgs::Args { args, order_by } => {
224 for arg in args {
225 self.visit_expr_mut(arg);
226 }
227 for expr in order_by {
228 self.visit_order_by_expr_mut(expr);
229 }
230 }
231 }
232 if let Some(over) = &mut func.over {
233 self.visit_window_spec_mut(over);
234 }
235 }
236
237 fn visit_table_factor_mut(&mut self, table_factor: &'ast mut TableFactor<Aug>) {
238 match table_factor {
239 TableFactor::Table { name, alias, .. } => {
240 self.visit_item_name_mut(name);
241 if let Some(alias) = alias {
242 self.visit_table_alias_mut(alias);
243 }
244 }
245 _ => visit_mut::visit_table_factor_mut(self, table_factor),
248 }
249 }
250 }
251
252 match &mut stmt {
263 Statement::CreateSource(CreateSourceStatement {
264 name,
265 in_cluster: _,
266 col_names: _,
267 connection: _,
268 format: _,
269 include_metadata: _,
270 envelope: _,
271 if_not_exists,
272 key_constraint: _,
273 with_options: _,
274 external_references: _,
275 progress_subsource: _,
276 }) => {
277 *name = allocate_name(name)?;
278 *if_not_exists = false;
279 }
280
281 Statement::CreateSubsource(CreateSubsourceStatement {
282 name,
283 columns,
284 constraints: _,
285 of_source: _,
286 if_not_exists,
287 with_options: _,
288 }) => {
289 *name = allocate_name(name)?;
290 let mut normalizer = QueryNormalizer::new();
291 for c in columns {
292 normalizer.visit_column_def_mut(c);
293 }
294 if let Some(err) = normalizer.err {
295 return Err(err);
296 }
297 *if_not_exists = false;
298 }
299
300 Statement::CreateTableFromSource(CreateTableFromSourceStatement {
301 name,
302 columns,
303 constraints: _,
304 external_reference: _,
305 source: _,
306 if_not_exists,
307 format: _,
308 include_metadata: _,
309 envelope: _,
310 with_options: _,
311 }) => {
312 *name = allocate_name(name)?;
313 let mut normalizer = QueryNormalizer::new();
314 if let TableFromSourceColumns::Defined(columns) = columns {
315 for c in columns {
316 normalizer.visit_column_def_mut(c);
317 }
318 }
319 if let Some(err) = normalizer.err {
320 return Err(err);
321 }
322 *if_not_exists = false;
323 }
324
325 Statement::CreateTable(CreateTableStatement {
326 name,
327 columns,
328 constraints: _,
329 if_not_exists,
330 temporary,
331 with_options: _,
332 }) => {
333 *name = if *temporary {
334 allocate_temporary_name(name)?
335 } else {
336 allocate_name(name)?
337 };
338 let mut normalizer = QueryNormalizer::new();
339 for c in columns {
340 normalizer.visit_column_def_mut(c);
341 }
342 if let Some(err) = normalizer.err {
343 return Err(err);
344 }
345 *if_not_exists = false;
346 }
347
348 Statement::CreateWebhookSource(CreateWebhookSourceStatement {
349 name,
350 is_table: _,
351 if_not_exists,
352 include_headers: _,
353 body_format: _,
354 validate_using: _,
355 in_cluster: _,
356 }) => {
357 *name = allocate_name(name)?;
358 *if_not_exists = false;
359 }
360
361 Statement::CreateSink(CreateSinkStatement {
362 name,
363 in_cluster: _,
364 from: _,
365 connection: _,
366 format: _,
367 envelope: _,
368 mode: _,
369 with_options: _,
370 if_not_exists,
371 }) => {
372 if let Some(name) = name {
373 *name = allocate_name(name)?;
374 }
375 *if_not_exists = false;
376 }
377
378 Statement::CreateView(CreateViewStatement {
379 temporary,
380 if_exists,
381 definition:
382 ViewDefinition {
383 name,
384 query,
385 columns: _,
386 },
387 }) => {
388 *name = if *temporary {
389 allocate_temporary_name(name)?
390 } else {
391 allocate_name(name)?
392 };
393 {
394 let mut normalizer = QueryNormalizer::new();
395 normalizer.visit_query_mut(query);
396 if let Some(err) = normalizer.err {
397 return Err(err);
398 }
399 }
400 *if_exists = IfExistsBehavior::Error;
401 }
402
403 Statement::CreateMaterializedView(CreateMaterializedViewStatement {
404 if_exists,
405 name,
406 columns: _,
407 replacement_for: _,
408 in_cluster: _,
409 in_cluster_replica: _,
410 query,
411 with_options: _,
412 as_of: _,
413 }) => {
414 *name = allocate_name(name)?;
415 {
416 let mut normalizer = QueryNormalizer::new();
417 normalizer.visit_query_mut(query);
418 if let Some(err) = normalizer.err {
419 return Err(err);
420 }
421 }
422 *if_exists = IfExistsBehavior::Error;
423 }
424
425 Statement::CreateIndex(CreateIndexStatement {
426 name: _,
427 in_cluster: _,
428 on_name: _,
429 key_parts,
430 with_options: _,
431 if_not_exists,
432 }) => {
433 let mut normalizer = QueryNormalizer::new();
434 if let Some(key_parts) = key_parts {
435 for key_part in key_parts {
436 normalizer.visit_expr_mut(key_part);
437 if let Some(err) = normalizer.err {
438 return Err(err);
439 }
440 }
441 }
442 *if_not_exists = false;
443 }
444
445 Statement::CreateType(CreateTypeStatement { name, as_type }) => {
446 *name = allocate_name(name)?;
447 let mut normalizer = QueryNormalizer::new();
448 normalizer.visit_create_type_as_mut(as_type);
449 if let Some(err) = normalizer.err {
450 return Err(err);
451 }
452 }
453 Statement::CreateSecret(CreateSecretStatement {
454 name,
455 if_not_exists,
456 value: _,
457 }) => {
458 *name = allocate_name(name)?;
459 *if_not_exists = false;
460 }
461 Statement::CreateConnection(CreateConnectionStatement {
462 name,
463 connection_type: _,
464 values,
465 with_options,
466 if_not_exists,
467 }) => {
468 *name = allocate_name(name)?;
469 *if_not_exists = false;
470
471 values.sort();
472
473 with_options
476 .retain(|o| o.name != mz_sql_parser::ast::CreateConnectionOptionName::Validate);
477 }
478
479 _ => bail_internal!("unexpected statement type for normalization"),
480 }
481
482 Ok(stmt.to_ast_string_stable())
483}
484
485macro_rules! generate_extracted_config {
510 (
512 $option_ty:ty, [$($processed:tt)*],
513 ($option_name:path, $t:ty), $($tail:tt),*
514 ) => {
515 generate_extracted_config!(
516 $option_ty,
517 [$($processed)* ($option_name, Option::<$t>, None, false)],
518 $($tail),*
519 );
520 };
521 (
523 $option_ty:ty, [$($processed:tt)*],
524 ($option_name:path, $t:ty)
525 ) => {
526 generate_extracted_config!(
527 $option_ty,
528 [$($processed)* ($option_name, Option::<$t>, None, false)]
529 );
530 };
531 (
533 $option_ty:ty, [$($processed:tt)*],
534 ($option_name:path, $t:ty, Default($v:expr)), $($tail:tt),*
535 ) => {
536 generate_extracted_config!(
537 $option_ty,
538 [$($processed)* ($option_name, $t, $v, false)],
539 $($tail),*
540 );
541 };
542 (
544 $option_ty:ty, [$($processed:tt)*],
545 ($option_name:path, $t:ty, Default($v:expr))
546 ) => {
547 generate_extracted_config!(
548 $option_ty,
549 [$($processed)* ($option_name, $t, $v, false)]
550 );
551 };
552 (
554 $option_ty:ty, [$($processed:tt)*],
555 ($option_name:path, $t:ty, AllowMultiple), $($tail:tt),*
556 ) => {
557 generate_extracted_config!(
558 $option_ty,
559 [$($processed)* ($option_name, $t, vec![], true)],
560 $($tail),*
561 );
562 };
563 (
565 $option_ty:ty, [$($processed:tt)*],
566 ($option_name:path, $t:ty, AllowMultiple)
567 ) => {
568 generate_extracted_config!(
569 $option_ty,
570 [$($processed)* ($option_name, $t, vec![], true)]
571 );
572 };
573 ($option_ty:ty, [$(($option_name:path, $t:ty, $v:expr, $allow_multiple:literal))+]) => {
574 paste::paste! {
575 #[derive(Debug)]
576 pub struct [<$option_ty Extracted>] {
577 pub(crate) seen: ::std::collections::BTreeSet::<[<$option_ty Name>]>,
578 $(
579 pub [<$option_name:snake>]: generate_extracted_config!(
580 @ifty $allow_multiple,
581 Vec::<$t>,
582 $t
583 ),
584 )*
585 }
586
587 impl std::default::Default for [<$option_ty Extracted>] {
588 fn default() -> Self {
589 [<$option_ty Extracted>] {
590 seen: ::std::collections::BTreeSet::<[<$option_ty Name>]>::new(),
591 $(
592 [<$option_name:snake>]: <generate_extracted_config!(
593 @ifty $allow_multiple,
594 Vec::<$t>,
595 $t
596 )>::from($v),
597 )*
598 }
599 }
600 }
601
602 impl std::convert::TryFrom<Vec<$option_ty<Aug>>>
603 for [<$option_ty Extracted>]
604 {
605 type Error = $crate::plan::PlanError;
606 fn try_from(
607 v: Vec<$option_ty<Aug>>,
608 ) -> Result<[<$option_ty Extracted>], Self::Error> {
609 use [<$option_ty Name>]::*;
610 let mut extracted = [<$option_ty Extracted>]::default();
611 for option in v {
612 match option.name {
613 $(
614 $option_name => {
615 if !$allow_multiple
616 && !extracted.seen.insert(option.name.clone())
617 {
618 sql_bail!(
619 "{} specified more than once",
620 option.name.to_ast_string_simple(),
621 );
622 }
623 let val: $t = $crate::plan::with_options
624 ::TryFromValue::try_from_value(option.value)
625 .map_err(|e| sql_err!(
626 "invalid {}: {}",
627 option.name.to_ast_string_simple(),
628 e,
629 ))?;
630 generate_extracted_config!(
631 @ifexpr $allow_multiple,
632 extracted.[<$option_name:snake>].push(val),
633 extracted.[<$option_name:snake>] = val
634 );
635 }
636 )*
637 }
638 }
639 Ok(extracted)
640 }
641 }
642
643 impl [<$option_ty Extracted>] {
644 #[allow(unused)]
645 fn into_values(
646 self,
647 catalog: &dyn crate::catalog::SessionCatalog,
648 ) -> Vec<$option_ty<Aug>> {
649 use [<$option_ty Name>]::*;
650 let mut options = Vec::new();
651 $(
652 let value = self.[<$option_name:snake>];
653 let values: Vec<_> = generate_extracted_config!(
654 @ifexpr $allow_multiple,
655 value,
656 Vec::from([value])
657 );
658 for value in values {
659 let maybe_value = <$t as $crate::plan::with_options::TryFromValue<
663 Option<mz_sql_parser::ast::WithOptionValue<$crate::names::Aug>>
664 >>::try_into_value(value, catalog);
665 match maybe_value {
666 Some(value) => {
667 let option = $option_ty {name: $option_name, value};
668 options.push(option);
669 },
670 None => (),
671 }
672 }
673 )*
674 options
675 }
676 }
677 }
678 };
679 ($option_ty:ty, $($h:tt),+) => {
680 generate_extracted_config!{$option_ty, [], $($h),+}
681 };
682 (@ifexpr false, $lhs:expr, $rhs:expr) => {
685 $rhs
686 };
687 (@ifexpr true, $lhs:expr, $rhs:expr) => {
688 $lhs
689 };
690 (@ifty false, $lhs:ty, $rhs:ty) => {
691 $rhs
692 };
693 (@ifty true, $lhs:ty, $rhs:ty) => {
694 $lhs
695 };
696}
697
698pub(crate) use generate_extracted_config;