1use crate::ast_transforms::get_output_column_names_for_dialect;
9use crate::dialects::{Dialect, DialectType};
10use crate::expressions::{DataType, Expression, Identifier, JoinKind, TableRef, With};
11use crate::lineage::{LineageNode, ScopedLineage};
12use crate::optimizer::annotate_types::annotate_types;
13use crate::optimizer::qualify_schema_aware_expression;
14use crate::schema::{MappingSchema, Schema};
15use crate::scope::{build_scope, Scope, SourceInfo, SourceKind};
16use crate::traversal::{
17 contains_aggregate, is_aggregate, is_arithmetic, is_comparison, is_logical, ExpressionWalk,
18};
19use crate::validation::{mapping_schema_from_validation_schema_with_dialect, ValidationSchema};
20use crate::{parse_one_with_options, ComplexityGuardOptions, Error, ParseOptions, Result};
21use serde::{Deserialize, Serialize};
22use std::cell::RefCell;
23use std::collections::{HashMap, HashSet};
24
25mod column_uses;
26
27#[derive(Debug, Clone, Serialize, Deserialize, Default)]
29#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
30pub struct AnalyzeQueryOptions {
31 pub complexity_guard: Option<ComplexityGuardOptions>,
33 pub dialect: DialectType,
35 pub schema: Option<ValidationSchema>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub struct QueryAnalysis {
43 pub shape: QueryShape,
44 pub ctes: Vec<String>,
45 pub cte_facts: Vec<CteFact>,
46 pub projections: Vec<ProjectionFact>,
47 pub relations: Vec<RelationFact>,
48 pub base_tables: Vec<RelationFact>,
49 pub star_projections: Vec<StarProjectionFact>,
50 pub set_operations: Vec<SetOperationFact>,
51 #[serde(default)]
53 pub column_uses: Vec<ColumnUseFact>,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58pub struct QuerySourceSpan {
59 pub start: usize,
60 pub end: usize,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "snake_case")]
66pub enum ColumnUseContext {
67 Join,
68 Filter,
69 Group,
70 Having,
71 Qualify,
72 WindowPartition,
73 WindowOrder,
74 WindowFrame,
75 Order,
76 AggregateOrder,
77 SetOperationFilter,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
84#[serde(rename_all = "camelCase")]
85pub struct ColumnUseReferenceFact {
86 #[serde(flatten)]
87 pub reference: ColumnReferenceFact,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub span: Option<QuerySourceSpan>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct ColumnUseFact {
97 pub context: ColumnUseContext,
98 pub scope_path: String,
99 pub expression_path: String,
100 pub expression_sql: String,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub span: Option<QuerySourceSpan>,
104 pub references: Vec<ColumnUseReferenceFact>,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
109#[serde(rename_all = "snake_case")]
110pub enum QueryShape {
111 Select,
112 SetOperation,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct ProjectionFact {
119 pub index: usize,
120 pub name: Option<String>,
121 pub is_star: bool,
122 pub star_table: Option<String>,
123 pub transform_kind: TransformKind,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub transform_function: Option<TransformFunctionFact>,
126 pub cast_type: Option<String>,
127 pub type_hint: Option<String>,
128 pub nullability: ProjectionNullability,
129 pub upstream: Vec<ColumnReferenceFact>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134#[serde(rename_all = "camelCase")]
135pub struct TransformFunctionFact {
136 pub name: String,
137 pub literal_args: Vec<String>,
138 pub column_args: Vec<ColumnReferenceFact>,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(rename_all = "camelCase")]
144pub struct CteFact {
145 pub name: String,
146 pub columns: Vec<String>,
147 pub body_sql: String,
148 pub output_columns: Vec<String>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153#[serde(rename_all = "camelCase")]
154pub struct StarProjectionFact {
155 pub index: usize,
156 pub table: Option<String>,
157 pub expanded_columns: Vec<String>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(rename_all = "camelCase")]
163pub struct ColumnReferenceFact {
164 pub source_name: Option<String>,
165 pub source_alias: Option<String>,
166 pub source_kind: SourceKind,
167 pub table: Option<String>,
168 pub column: String,
169 pub unqualified: bool,
170 pub confidence: ReferenceConfidence,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
177 pub path_transform_kind: Option<TransformKind>,
178 #[serde(default, skip_serializing_if = "Vec::is_empty")]
182 pub path_functions: Vec<String>,
183 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
185 pub path_window: bool,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct RelationFact {
192 pub name: String,
193 pub alias: Option<String>,
194 pub kind: SourceKind,
195 pub columns: Vec<String>,
196 pub catalog: Option<String>,
197 pub schema: Option<String>,
198 pub table: Option<String>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct SetOperationFact {
205 pub kind: String,
206 pub all: bool,
207 pub distinct: bool,
208 pub output_columns: Vec<String>,
209 pub branches: Vec<SetOperationBranchFact>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214#[serde(rename_all = "camelCase")]
215pub struct SetOperationBranchFact {
216 pub index: usize,
217 pub role: SetOperationBranchRole,
218 pub projections: Vec<ProjectionFact>,
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
223#[serde(rename_all = "snake_case")]
224pub enum SetOperationBranchRole {
225 Value,
226 Filter,
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
231#[serde(rename_all = "snake_case")]
232pub enum TransformKind {
233 Direct,
234 Cast,
235 Aggregation,
236 Constant,
237 Expression,
238 Star,
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
243#[serde(rename_all = "snake_case")]
244pub enum ReferenceConfidence {
245 Resolved,
246 Ambiguous,
247 Unknown,
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
252#[serde(rename_all = "snake_case")]
253pub enum ProjectionNullability {
254 NonNull,
255 Nullable,
256 Unknown,
257}
258
259pub fn analyze_query(sql: &str, options: AnalyzeQueryOptions) -> Result<QueryAnalysis> {
261 let mut expression = parse_one_with_options(
262 sql,
263 options.dialect,
264 &ParseOptions {
265 complexity_guard: options.complexity_guard,
266 },
267 )?;
268 expression = effective_query(expression);
269 ensure_query(&expression)?;
270 expression = crate::binding::bind_lambdas(expression, options.dialect);
271 let original_expression = expression.clone();
272
273 let mapping_schema = options
274 .schema
275 .as_ref()
276 .map(|schema| analysis_mapping_schema(schema, options.dialect));
277 let schema_info = options.schema.as_ref().map(AnalysisSchemaInfo::from_schema);
278 let cte_facts = top_level_cte_facts(
279 &original_expression,
280 options.dialect,
281 options.complexity_guard,
282 )?;
283 if let Some(schema) = mapping_schema.as_ref() {
284 use crate::optimizer::qualify_columns::QualifyColumnsError;
285 expression = match qualify_schema_aware_expression(
286 expression.clone(),
287 schema,
288 Some(options.dialect),
289 ) {
290 Ok(qualified) => qualified,
291 Err(
294 QualifyColumnsError::UnknownTable(_)
295 | QualifyColumnsError::UnknownColumn(_)
296 | QualifyColumnsError::AmbiguousColumn(_)
297 | QualifyColumnsError::ColumnNotResolved { .. },
298 ) => expression,
299 Err(error) => {
300 return Err(Error::internal(format!(
301 "query analysis qualification failed: {error}"
302 )))
303 }
304 };
305 }
306
307 crate::lineage::expand_cte_stars(
308 &mut expression,
309 mapping_schema.as_ref().map(|schema| schema as &dyn Schema),
310 );
311 let star_projections = star_projection_facts(
312 &original_expression,
313 &expression,
314 mapping_schema.as_ref(),
315 options.dialect,
316 );
317 annotate_types(
318 &mut expression,
319 mapping_schema.as_ref().map(|schema| schema as &dyn Schema),
320 Some(options.dialect),
321 );
322
323 let scope = build_scope(&expression);
324 let original_scope = build_scope(&original_expression);
325 let empty_schema = MappingSchema::with_dialect(options.dialect);
326 let mut uncertain_columns = HashMap::new();
327 column_uses::collect_uncertain_occurrences(
328 &original_scope,
329 mapping_schema.as_ref().unwrap_or(&empty_schema),
330 &mut uncertain_columns,
331 );
332 let nullability_context = NullabilityContext::new(
333 &scope,
334 schema_info.as_ref(),
335 mapping_schema.as_ref(),
336 options.dialect,
337 &uncertain_columns,
338 );
339 let shape = if is_set_operation(&expression) {
340 QueryShape::SetOperation
341 } else {
342 QueryShape::Select
343 };
344
345 Ok(QueryAnalysis {
346 shape,
347 ctes: collect_cte_names(&expression),
348 cte_facts,
349 projections: projection_facts_for_query(
350 &expression,
351 &scope,
352 options.dialect,
353 &nullability_context,
354 ),
355 relations: relation_facts(&scope, mapping_schema.as_ref(), options.dialect),
356 base_tables: base_table_facts(&scope, mapping_schema.as_ref(), options.dialect),
357 star_projections,
358 set_operations: set_operation_facts(
359 &expression,
360 &scope,
361 options.dialect,
362 &nullability_context,
363 ),
364 column_uses: column_uses::collect(
365 &original_scope,
366 &scope,
367 mapping_schema.as_ref(),
368 options.dialect,
369 &uncertain_columns,
370 options.complexity_guard,
371 ),
372 })
373}
374
375fn analysis_mapping_schema(schema: &ValidationSchema, dialect: DialectType) -> MappingSchema {
376 mapping_schema_from_validation_schema_with_dialect(schema, dialect)
377}
378
379fn validation_table_names(table: &crate::validation::SchemaTable) -> Vec<String> {
380 let mut names = Vec::new();
381
382 names.push(table.name.to_ascii_lowercase());
383 if let Some(schema_name) = &table.schema {
384 names.push(format!(
385 "{}.{}",
386 schema_name.to_ascii_lowercase(),
387 table.name.to_ascii_lowercase()
388 ));
389 }
390 for alias in &table.aliases {
391 names.push(alias.to_ascii_lowercase());
392 }
393
394 names.sort();
395 names.dedup();
396 names
397}
398
399#[derive(Debug, Clone)]
400struct AnalysisColumnInfo {
401 nullable: Option<bool>,
402 primary_key: bool,
403}
404
405#[derive(Debug, Clone)]
406struct AnalysisSchemaInfo {
407 columns: HashMap<(String, String), AnalysisColumnInfo>,
408}
409
410impl AnalysisSchemaInfo {
411 fn from_schema(schema: &ValidationSchema) -> Self {
412 let mut columns = HashMap::new();
413
414 for table in &schema.tables {
415 let table_names = validation_table_names(table);
416 let primary_keys: HashSet<String> = table
417 .primary_key
418 .iter()
419 .map(|column| column.to_ascii_lowercase())
420 .collect();
421
422 for column in &table.columns {
423 let info = AnalysisColumnInfo {
424 nullable: column.nullable,
425 primary_key: column.primary_key
426 || primary_keys.contains(&column.name.to_ascii_lowercase()),
427 };
428
429 for table_name in &table_names {
430 columns.insert(
431 (
432 normalize_lookup_name(table_name),
433 normalize_lookup_name(&column.name),
434 ),
435 info.clone(),
436 );
437 }
438 }
439 }
440
441 Self { columns }
442 }
443
444 fn column(&self, table: &str, column: &str) -> Option<&AnalysisColumnInfo> {
445 self.columns
446 .get(&(normalize_lookup_name(table), normalize_lookup_name(column)))
447 }
448}
449
450struct NullabilityContext<'a> {
451 lineage: RefCell<HashMap<*const Scope, ScopedLineage>>,
452 output_types: RefCell<crate::optimizer::set_operation_types::OutputResolver>,
453 schema: Option<&'a AnalysisSchemaInfo>,
454 mapping_schema: Option<&'a MappingSchema>,
455 empty_schema: MappingSchema,
456 dialect: DialectType,
457 scopes: Vec<NullabilityScope<'a>>,
458 scope_ids: HashMap<*const Scope, usize>,
459 outputs: RefCell<HashMap<(usize, usize), ProjectionNullability>>,
460 resolving: RefCell<HashSet<(usize, usize)>>,
461 uncertain_columns: &'a HashMap<(usize, usize), ReferenceConfidence>,
462}
463
464struct NullabilityScope<'a> {
465 scope: &'a Scope,
466 selected: Scope,
467 bindings: HashMap<String, &'a Expression>,
468 ctes: HashMap<String, usize>,
469 derived: Vec<usize>,
470 branches: Vec<usize>,
471 nullable_sources: HashSet<String>,
472}
473
474impl<'a> NullabilityContext<'a> {
475 fn new(
476 scope: &'a Scope,
477 schema: Option<&'a AnalysisSchemaInfo>,
478 mapping_schema: Option<&'a MappingSchema>,
479 dialect: DialectType,
480 uncertain_columns: &'a HashMap<(usize, usize), ReferenceConfidence>,
481 ) -> Self {
482 let mut context = Self {
483 lineage: RefCell::new(HashMap::new()),
484 output_types: RefCell::new(crate::optimizer::set_operation_types::OutputResolver::new(
485 dialect,
486 )),
487 schema,
488 mapping_schema,
489 empty_schema: MappingSchema::with_dialect(dialect),
490 dialect,
491 scopes: Vec::new(),
492 scope_ids: HashMap::new(),
493 outputs: RefCell::new(HashMap::new()),
494 resolving: RefCell::new(HashSet::new()),
495 uncertain_columns,
496 };
497 context.index_scope(scope, HashMap::new(), None);
498 context
499 }
500
501 fn index_scope(
504 &mut self,
505 scope: &'a Scope,
506 mut ctes: HashMap<String, usize>,
507 recursive_name: Option<&Identifier>,
508 ) -> usize {
509 let id = self.scopes.len();
510 self.scope_ids.insert(scope, id);
511 if let Some(name) = recursive_name {
512 ctes.insert(
513 crate::set_operation::identifier_key(name, Some(self.dialect)),
514 id,
515 );
516 }
517 let query = crate::scope::scope_query(&scope.expression);
518 let bindings: HashMap<_, _> = if let Expression::Select(select) = query {
519 select
520 .from
521 .iter()
522 .flat_map(|from| &from.expressions)
523 .chain(select.joins.iter().map(|join| &join.this))
524 .filter_map(|expression| {
525 expression_source_name(expression).map(|name| (name, expression))
526 })
527 .collect()
528 } else {
529 HashMap::new()
530 };
531 self.scopes.push(NullabilityScope {
532 scope,
533 selected: crate::scope::selected_reference_scope(scope),
534 bindings,
535 ctes: HashMap::new(),
536 derived: Vec::new(),
537 branches: Vec::new(),
538 nullable_sources: nullable_source_names(query, self.dialect),
539 });
540 let recursive = with_clause(crate::scope::scope_query(&scope.expression))
541 .is_some_and(|with| with.recursive);
542 for child in &scope.cte_scopes {
543 if let Expression::Cte(cte) = &child.expression {
544 let name = &cte.alias;
545 let child_id = self.index_scope(child, ctes.clone(), recursive.then_some(name));
546 ctes.insert(
547 crate::set_operation::identifier_key(name, Some(self.dialect)),
548 child_id,
549 );
550 }
551 }
552 self.scopes[id].ctes = ctes.clone();
553 for child in &scope.derived_table_scopes {
554 let child_id = self.index_scope(child, ctes.clone(), None);
555 self.scopes[id].derived.push(child_id);
556 }
557 for child in &scope.union_scopes {
558 let child_id = self.index_scope(child, ctes.clone(), None);
559 self.scopes[id].branches.push(child_id);
560 }
561 id
562 }
563}
564
565fn top_level_cte_facts(
566 expression: &Expression,
567 dialect: DialectType,
568 guard: Option<ComplexityGuardOptions>,
569) -> Result<Vec<CteFact>> {
570 let Some(with_clause) = with_clause(expression) else {
571 return Ok(Vec::new());
572 };
573
574 with_clause
575 .ctes
576 .iter()
577 .map(|cte| {
578 Ok(CteFact {
579 name: cte.alias.name.clone(),
580 columns: cte
581 .columns
582 .iter()
583 .map(|column| column.name.clone())
584 .collect(),
585 body_sql: Dialect::get(dialect).generate_with_guard(&cte.this, guard)?,
586 output_columns: get_output_column_names_for_dialect(&cte.this, Some(dialect)),
587 })
588 })
589 .collect()
590}
591
592fn star_projection_facts(
595 expression: &Expression,
596 resolved: &Expression,
597 mapping_schema: Option<&MappingSchema>,
598 dialect: DialectType,
599) -> Vec<StarProjectionFact> {
600 let scope = build_scope(resolved);
601 let ordered_sources = ordered_source_names_for_query(resolved);
602
603 select_expressions_for_query(expression)
604 .iter()
605 .enumerate()
606 .filter_map(|(index, projection)| {
607 let inner = unwrap_projection_alias(projection);
608 if !projection_is_star(inner) {
609 return None;
610 }
611
612 let table = projection_star_table(inner);
613 let mut expanded_columns = match inner {
614 Expression::Star(star) if star.has_except_pattern() => Vec::new(),
615 _ => expanded_star_columns(
616 table.as_deref(),
617 &scope,
618 &ordered_sources,
619 mapping_schema,
620 dialect,
621 ),
622 };
623 if let Expression::Star(star) = inner {
624 expanded_columns = expanded_columns
625 .into_iter()
626 .filter(|column| !star.excludes(column, Some(dialect)))
627 .map(|column| {
628 star.renamed(&column, Some(dialect))
629 .map_or(column, |new| new.name)
630 })
631 .collect();
632 }
633
634 Some(StarProjectionFact {
635 index,
636 table,
637 expanded_columns,
638 })
639 })
640 .collect()
641}
642
643fn expanded_star_columns(
644 star_table: Option<&str>,
645 scope: &Scope,
646 ordered_sources: &[String],
647 mapping_schema: Option<&MappingSchema>,
648 dialect: DialectType,
649) -> Vec<String> {
650 let mut columns = Vec::new();
651 let mut source_names: Vec<String> = if ordered_sources.is_empty() {
652 let mut names: Vec<_> = scope.sources.keys().cloned().collect();
653 names.sort();
654 names
655 } else {
656 ordered_sources.to_vec()
657 };
658
659 source_names.dedup();
660
661 let empty_schema = MappingSchema::with_dialect(dialect);
662 let mut resolver = crate::resolver::Resolver::new(
663 scope,
664 mapping_schema.map_or(&empty_schema as &dyn Schema, |schema| schema),
665 false,
666 );
667 for source_name in source_names {
668 let Some(source) = scope.sources.get(&source_name) else {
669 continue;
670 };
671
672 if let Some(star_table) = star_table {
673 let matches = source_name.eq_ignore_ascii_case(star_table)
674 || source
675 .alias
676 .as_deref()
677 .is_some_and(|alias| alias.eq_ignore_ascii_case(star_table))
678 || source_table_name(source)
679 .is_some_and(|table| table.eq_ignore_ascii_case(star_table));
680
681 if !matches {
682 continue;
683 }
684 }
685
686 match &source.expression {
687 Expression::Pivot(_) | Expression::Unpivot(_) => columns.extend(
689 resolver
690 .get_source_columns(&source_name)
691 .unwrap_or_default(),
692 ),
693 _ => columns.extend(source_columns(source, mapping_schema, dialect)),
694 }
695 }
696
697 columns
698}
699
700fn ordered_source_names_for_query(expression: &Expression) -> Vec<String> {
701 match expression {
702 Expression::Select(select) => ordered_source_names_for_select(select),
703 Expression::Union(union) => ordered_source_names_for_query(&union.left),
704 Expression::Intersect(intersect) => ordered_source_names_for_query(&intersect.left),
705 Expression::Except(except) => ordered_source_names_for_query(&except.left),
706 Expression::Subquery(subquery) => ordered_source_names_for_query(&subquery.this),
707 _ => Vec::new(),
708 }
709}
710
711fn ordered_source_names_for_select(select: &crate::expressions::Select) -> Vec<String> {
712 let mut sources = Vec::new();
713
714 if let Some(from) = &select.from {
715 for expression in &from.expressions {
716 if let Some(source_name) = expression_source_name(expression) {
717 sources.push(source_name);
718 }
719 }
720 }
721
722 for join in &select.joins {
723 if let Some(source_name) = expression_source_name(&join.this) {
724 sources.push(source_name);
725 }
726 }
727
728 sources
729}
730
731fn nullable_source_names(expression: &Expression, dialect: DialectType) -> HashSet<String> {
732 match expression {
733 Expression::Select(select) => nullable_source_names_for_select(select, dialect),
734 Expression::Union(union) => nullable_source_names(&union.left, dialect),
735 Expression::Intersect(intersect) => nullable_source_names(&intersect.left, dialect),
736 Expression::Except(except) => nullable_source_names(&except.left, dialect),
737 Expression::Subquery(subquery) => nullable_source_names(&subquery.this, dialect),
738 _ => HashSet::new(),
739 }
740}
741
742fn nullable_source_names_for_select(
743 select: &crate::expressions::Select,
744 dialect: DialectType,
745) -> HashSet<String> {
746 let mut nullable = HashSet::new();
747 let mut left_sources = Vec::new();
748
749 if let Some(from) = &select.from {
750 for expression in &from.expressions {
751 if let Some(identifier) = expression_source_identifier(expression) {
752 left_sources.push(crate::set_operation::identifier_key(
753 identifier,
754 Some(dialect),
755 ));
756 }
757 }
758 }
759
760 for join in &select.joins {
761 let right_source = expression_source_identifier(&join.this)
762 .map(|identifier| crate::set_operation::identifier_key(identifier, Some(dialect)));
763
764 if join_nullable_left(join.kind) {
765 for source_name in &left_sources {
766 nullable.insert(source_name.clone());
767 }
768 }
769
770 if join_nullable_right(join.kind) {
771 if let Some(source_name) = &right_source {
772 nullable.insert(source_name.clone());
773 }
774 }
775
776 if let Some(source_name) = right_source {
777 left_sources.push(source_name);
778 }
779 }
780
781 nullable
782}
783
784fn join_nullable_left(kind: JoinKind) -> bool {
785 matches!(
786 kind,
787 JoinKind::Right
788 | JoinKind::NaturalRight
789 | JoinKind::AsOfRight
790 | JoinKind::Full
791 | JoinKind::NaturalFull
792 | JoinKind::Outer
793 )
794}
795
796fn join_nullable_right(kind: JoinKind) -> bool {
797 matches!(
798 kind,
799 JoinKind::Left
800 | JoinKind::NaturalLeft
801 | JoinKind::AsOfLeft
802 | JoinKind::LeftLateral
803 | JoinKind::OuterApply
804 | JoinKind::LeftArray
805 | JoinKind::Full
806 | JoinKind::NaturalFull
807 | JoinKind::Outer
808 )
809}
810
811fn expression_source_name(expression: &Expression) -> Option<String> {
812 expression_source_identifier(expression).map(|identifier| identifier.name.clone())
813}
814
815fn expression_source_identifier(expression: &Expression) -> Option<&Identifier> {
816 match expression {
817 Expression::Table(table) => Some(table.alias.as_ref().unwrap_or(&table.name)),
818 Expression::Subquery(subquery) => subquery.alias.as_ref(),
819 Expression::Alias(alias) => Some(&alias.alias),
820 Expression::Cte(cte) => Some(&cte.alias),
821 _ => None,
822 }
823}
824
825fn normalize_lookup_name(name: &str) -> String {
826 name.to_ascii_lowercase()
827}
828
829fn effective_query(expression: Expression) -> Expression {
830 match expression {
831 Expression::Prepare(prepare) => prepare.statement,
832 Expression::Subquery(subquery) if subquery.alias.is_none() => subquery.this,
833 other => other,
834 }
835}
836
837fn ensure_query(expression: &Expression) -> Result<()> {
838 if matches!(
839 expression,
840 Expression::Select(_)
841 | Expression::Union(_)
842 | Expression::Intersect(_)
843 | Expression::Except(_)
844 ) {
845 Ok(())
846 } else {
847 Err(Error::internal(
848 "analyze_query requires a SELECT or set operation query",
849 ))
850 }
851}
852
853fn is_set_operation(expression: &Expression) -> bool {
854 matches!(
855 expression,
856 Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
857 )
858}
859
860fn collect_cte_names(expression: &Expression) -> Vec<String> {
861 let mut names = Vec::new();
862 let mut seen = HashSet::new();
863 collect_cte_names_inner(expression, &mut names, &mut seen);
864 names
865}
866
867fn collect_cte_names_inner(
868 expression: &Expression,
869 names: &mut Vec<String>,
870 seen: &mut HashSet<String>,
871) {
872 if let Some(with_clause) = with_clause(expression) {
873 collect_with_names(with_clause, names, seen);
874 }
875
876 match expression {
877 Expression::Union(union) => {
878 collect_cte_names_inner(&union.left, names, seen);
879 collect_cte_names_inner(&union.right, names, seen);
880 }
881 Expression::Intersect(intersect) => {
882 collect_cte_names_inner(&intersect.left, names, seen);
883 collect_cte_names_inner(&intersect.right, names, seen);
884 }
885 Expression::Except(except) => {
886 collect_cte_names_inner(&except.left, names, seen);
887 collect_cte_names_inner(&except.right, names, seen);
888 }
889 Expression::Subquery(subquery) => collect_cte_names_inner(&subquery.this, names, seen),
890 _ => {}
891 }
892}
893
894fn collect_with_names(with_clause: &With, names: &mut Vec<String>, seen: &mut HashSet<String>) {
895 for cte in &with_clause.ctes {
896 if seen.insert(cte.alias.name.clone()) {
897 names.push(cte.alias.name.clone());
898 }
899 collect_cte_names_inner(&cte.this, names, seen);
900 }
901}
902
903fn with_clause(expression: &Expression) -> Option<&With> {
904 match expression {
905 Expression::Select(select) => select.with.as_ref(),
906 Expression::Union(union) => union.with.as_ref(),
907 Expression::Intersect(intersect) => intersect.with.as_ref(),
908 Expression::Except(except) => except.with.as_ref(),
909 _ => None,
910 }
911}
912
913fn projection_facts_for_query(
914 expression: &Expression,
915 scope: &Scope,
916 dialect: DialectType,
917 nullability_context: &NullabilityContext<'_>,
918) -> Vec<ProjectionFact> {
919 let expressions = projection_sources_for_query(expression, dialect);
920 let names = get_output_column_names_for_dialect(expression, Some(dialect));
921 let mut type_query = expression;
922 loop {
923 type_query = match type_query {
924 Expression::Subquery(subquery) => &subquery.this,
925 Expression::Paren(paren) => &paren.this,
926 Expression::Annotated(annotated) => &annotated.this,
927 _ => break,
928 };
929 }
930 let resolved_outputs = is_set_operation(type_query).then(|| {
931 nullability_context
932 .output_types
933 .borrow_mut()
934 .resolve(expression)
935 });
936
937 expressions
938 .iter()
939 .enumerate()
940 .map(|(index, (projection, null_padded))| {
941 let mut fact = projection_fact(
942 index,
943 names
944 .get(index)
945 .cloned()
946 .or_else(|| projection_name(projection)),
947 projection,
948 expression,
949 scope,
950 dialect,
951 nullability_context,
952 );
953 if *null_padded {
954 fact.nullability = ProjectionNullability::Nullable;
955 }
956 if let Some(outputs) = &resolved_outputs {
957 let output = outputs.get(index);
958 fact.type_hint = output
959 .and_then(|output| output.data_type())
960 .and_then(|data_type| render_data_type(data_type, dialect));
961 fact.cast_type = output
962 .and_then(|output| output.cast_type.as_ref())
963 .and_then(|data_type| render_data_type(data_type, dialect));
964 }
965 fact
966 })
967 .collect()
968}
969
970fn projection_sources_for_query(
973 expression: &Expression,
974 dialect: DialectType,
975) -> Vec<(&Expression, bool)> {
976 match expression {
977 Expression::Subquery(subquery) => {
978 return projection_sources_for_query(&subquery.this, dialect)
979 }
980 Expression::Paren(paren) => return projection_sources_for_query(&paren.this, dialect),
981 Expression::Annotated(annotated) => {
982 return projection_sources_for_query(&annotated.this, dialect)
983 }
984 _ => {}
985 }
986 match crate::set_operation::set_operation_layout(expression, Some(dialect)) {
987 Ok(Some(layout)) => layout
988 .outputs
989 .iter()
990 .filter_map(|output| {
991 let null_padded = output.left_ordinal.is_none() || output.right_ordinal.is_none();
992 output
993 .left_ordinal
994 .and_then(|ordinal| {
995 projection_source_for_ordinal(
996 set_operation_left(expression)?,
997 ordinal,
998 dialect,
999 )
1000 })
1001 .or_else(|| {
1002 output.right_ordinal.and_then(|ordinal| {
1003 projection_source_for_ordinal(
1004 set_operation_right(expression)?,
1005 ordinal,
1006 dialect,
1007 )
1008 })
1009 })
1010 .map(|(projection, nested_null_padded)| {
1011 (projection, null_padded || nested_null_padded)
1012 })
1013 })
1014 .collect(),
1015 _ => select_expressions_for_query(expression)
1016 .into_iter()
1017 .map(|projection| (projection, false))
1018 .collect(),
1019 }
1020}
1021
1022fn projection_source_for_ordinal(
1023 expression: &Expression,
1024 ordinal: usize,
1025 dialect: DialectType,
1026) -> Option<(&Expression, bool)> {
1027 match crate::set_operation::set_operation_layout(expression, Some(dialect)) {
1028 Ok(Some(layout)) => {
1029 let output = layout.outputs.get(ordinal)?;
1030 let null_padded = output.left_ordinal.is_none() || output.right_ordinal.is_none();
1031 output
1032 .left_ordinal
1033 .and_then(|child_ordinal| {
1034 projection_source_for_ordinal(
1035 set_operation_left(expression)?,
1036 child_ordinal,
1037 dialect,
1038 )
1039 })
1040 .or_else(|| {
1041 output.right_ordinal.and_then(|child_ordinal| {
1042 projection_source_for_ordinal(
1043 set_operation_right(expression)?,
1044 child_ordinal,
1045 dialect,
1046 )
1047 })
1048 })
1049 .map(|(projection, nested_null_padded)| {
1050 (projection, null_padded || nested_null_padded)
1051 })
1052 }
1053 _ => match expression {
1054 Expression::Select(select) => select
1055 .expressions
1056 .get(ordinal)
1057 .map(|projection| (projection, false)),
1058 Expression::Union(union) => {
1059 projection_source_for_ordinal(&union.left, ordinal, dialect)
1060 }
1061 Expression::Intersect(intersect) => {
1062 projection_source_for_ordinal(&intersect.left, ordinal, dialect)
1063 }
1064 Expression::Except(except) => {
1065 projection_source_for_ordinal(&except.left, ordinal, dialect)
1066 }
1067 Expression::Subquery(subquery) => {
1068 projection_source_for_ordinal(&subquery.this, ordinal, dialect)
1069 }
1070 Expression::Paren(paren) => {
1071 projection_source_for_ordinal(&paren.this, ordinal, dialect)
1072 }
1073 _ => None,
1074 },
1075 }
1076}
1077
1078fn set_operation_left(expression: &Expression) -> Option<&Expression> {
1079 match expression {
1080 Expression::Union(set_op) => Some(&set_op.left),
1081 Expression::Intersect(set_op) => Some(&set_op.left),
1082 Expression::Except(set_op) => Some(&set_op.left),
1083 _ => None,
1084 }
1085}
1086
1087fn set_operation_right(expression: &Expression) -> Option<&Expression> {
1088 match expression {
1089 Expression::Union(set_op) => Some(&set_op.right),
1090 Expression::Intersect(set_op) => Some(&set_op.right),
1091 Expression::Except(set_op) => Some(&set_op.right),
1092 _ => None,
1093 }
1094}
1095
1096fn select_expressions_for_query(expression: &Expression) -> Vec<&Expression> {
1097 match expression {
1098 Expression::Select(select) => select.expressions.iter().collect(),
1099 Expression::Union(union) => select_expressions_for_query(&union.left),
1100 Expression::Intersect(intersect) => select_expressions_for_query(&intersect.left),
1101 Expression::Except(except) => select_expressions_for_query(&except.left),
1102 Expression::Subquery(subquery) => select_expressions_for_query(&subquery.this),
1103 _ => Vec::new(),
1104 }
1105}
1106
1107fn projection_fact(
1108 index: usize,
1109 name: Option<String>,
1110 projection: &Expression,
1111 _query: &Expression,
1112 scope: &Scope,
1113 dialect: DialectType,
1114 nullability_context: &NullabilityContext<'_>,
1115) -> ProjectionFact {
1116 let inner = unwrap_projection_alias(projection);
1117 let is_star = projection_is_star(inner);
1118 let kind = transform_kind(inner);
1119 let mut lineage = nullability_context.lineage.borrow_mut();
1120 let prepared = lineage
1121 .entry(scope as *const Scope)
1122 .or_insert_with(|| ScopedLineage::new(scope.clone(), &[], dialect));
1123 let mut upstream = prepared
1125 .output(index)
1126 .ok()
1127 .filter(|node| !node.downstream.is_empty())
1128 .map(|node| {
1129 let uncertain = lineage_uncertainty(&node, nullability_context.uncertain_columns);
1130 let mut references =
1131 terminal_references_from_lineage(&node, Some(&PathTransform::new(dialect)));
1132 if let Some(confidence) = uncertain {
1133 for reference in &mut references {
1134 reference.confidence = confidence;
1135 }
1136 }
1137 references
1138 })
1139 .unwrap_or_else(|| fallback_column_references(inner, scope));
1140 if let Some(confidence) = expression_uncertainty(inner, nullability_context.uncertain_columns) {
1141 for reference in &mut upstream {
1142 reference.confidence = confidence;
1143 }
1144 }
1145 if upstream
1147 .iter()
1148 .any(|reference| reference.path_transform_kind.is_none())
1149 {
1150 let projection_path = PathTransform::new(dialect).step(inner);
1151 for reference in &mut upstream {
1152 if reference.path_transform_kind.is_none() {
1153 projection_path.apply(reference);
1154 }
1155 }
1156 }
1157 if let Some(schema) = nullability_context.mapping_schema {
1158 for reference in &mut upstream {
1159 if let Some(table) = &reference.table {
1160 if let Ok(columns) = schema.column_names(table) {
1161 if !columns.is_empty()
1162 && !columns.iter().any(|column| {
1163 column == "*"
1164 || crate::schema::normalize_name(column, Some(dialect), false, true)
1165 == crate::schema::normalize_name(
1166 &reference.column,
1167 Some(dialect),
1168 false,
1169 true,
1170 )
1171 })
1172 {
1173 reference.confidence = ReferenceConfidence::Unknown;
1174 }
1175 }
1176 }
1177 }
1178 }
1179
1180 let mut transform_function = transform_function_fact(inner, scope, dialect);
1181 if let Some(function) = &mut transform_function {
1182 for reference in &mut function.column_args {
1183 if let Some(upstream) = upstream.iter().find(|upstream| {
1184 upstream.column == reference.column && upstream.table == reference.table
1185 }) {
1186 reference.confidence = upstream.confidence;
1187 } else if upstream
1188 .iter()
1189 .any(|upstream| upstream.confidence != ReferenceConfidence::Resolved)
1190 {
1191 reference.confidence = ReferenceConfidence::Unknown;
1192 }
1193 }
1194 }
1195 ProjectionFact {
1196 index,
1197 name,
1198 is_star,
1199 star_table: projection_star_table(inner),
1200 transform_kind: kind,
1201 transform_function,
1202 cast_type: cast_type(inner, dialect),
1203 type_hint: projection
1204 .inferred_type()
1205 .or_else(|| inner.inferred_type())
1206 .filter(|data_type| **data_type != DataType::Unknown)
1207 .and_then(|data_type| render_data_type(data_type, dialect)),
1208 nullability: nullability_context
1209 .scope_ids
1210 .get(&(scope as *const Scope))
1211 .map(|id| nullability_context.output(*id, index, 0))
1212 .unwrap_or(ProjectionNullability::Unknown),
1213 upstream,
1214 }
1215}
1216
1217fn expression_uncertainty(
1218 expression: &Expression,
1219 uncertain: &HashMap<(usize, usize), ReferenceConfidence>,
1220) -> Option<ReferenceConfidence> {
1221 combine_uncertainty(
1222 crate::scope::walk_in_scope(expression, false).filter_map(|node| match node {
1223 Expression::Column(column) => column
1224 .span
1225 .or(column.name.span)
1226 .and_then(|span| uncertain.get(&(span.start, span.end)))
1227 .copied(),
1228 _ => None,
1229 }),
1230 )
1231}
1232
1233fn combine_uncertainty(
1234 confidences: impl Iterator<Item = ReferenceConfidence>,
1235) -> Option<ReferenceConfidence> {
1236 confidences.fold(None, |current, confidence| {
1237 Some(
1238 if current == Some(ReferenceConfidence::Ambiguous)
1239 || confidence == ReferenceConfidence::Ambiguous
1240 {
1241 ReferenceConfidence::Ambiguous
1242 } else {
1243 confidence
1244 },
1245 )
1246 })
1247}
1248
1249fn lineage_uncertainty(
1250 node: &LineageNode,
1251 uncertain: &HashMap<(usize, usize), ReferenceConfidence>,
1252) -> Option<ReferenceConfidence> {
1253 combine_uncertainty(
1254 node.walk()
1255 .filter_map(|node| expression_uncertainty(&node.expression, uncertain)),
1256 )
1257}
1258
1259fn transform_function_fact(
1260 expression: &Expression,
1261 scope: &Scope,
1262 dialect: DialectType,
1263) -> Option<TransformFunctionFact> {
1264 let mut matches = expression
1265 .find_all(|candidate| transform_function_fact_for_node(candidate, scope, dialect).is_some())
1266 .into_iter();
1267
1268 let first = matches.next()?;
1269 if matches.next().is_some() {
1270 return None;
1271 }
1272
1273 transform_function_fact_for_node(first, scope, dialect)
1274}
1275
1276fn transform_function_fact_for_node(
1277 expression: &Expression,
1278 scope: &Scope,
1279 dialect: DialectType,
1280) -> Option<TransformFunctionFact> {
1281 match expression {
1282 Expression::Function(function) => Some(transform_function_from_args(
1283 &function.name,
1284 &function.args,
1285 scope,
1286 dialect,
1287 )),
1288 Expression::AggregateFunction(function) => Some(transform_function_from_args(
1289 &function.name,
1290 &function.args,
1291 scope,
1292 dialect,
1293 )),
1294 Expression::DateTrunc(function) => Some(transform_function_from_parts(
1295 "DATE_TRUNC",
1296 vec![datetime_field_name(&function.unit)],
1297 vec![&function.this],
1298 scope,
1299 dialect,
1300 )),
1301 Expression::TimestampTrunc(function) => Some(transform_function_from_parts(
1302 "TIMESTAMP_TRUNC",
1303 vec![datetime_field_name(&function.unit)],
1304 vec![&function.this],
1305 scope,
1306 dialect,
1307 )),
1308 Expression::TimeTrunc(function) => {
1309 let mut args = vec![function.this.as_ref()];
1310 if let Some(zone) = function.zone.as_deref() {
1311 args.push(zone);
1312 }
1313 Some(transform_function_from_parts(
1314 "TIME_TRUNC",
1315 vec![function.unit.clone()],
1316 args,
1317 scope,
1318 dialect,
1319 ))
1320 }
1321 Expression::Extract(function) => Some(transform_function_from_parts(
1322 "EXTRACT",
1323 vec![datetime_field_name(&function.field)],
1324 vec![&function.this],
1325 scope,
1326 dialect,
1327 )),
1328 Expression::DateAdd(function) => Some(transform_function_from_parts(
1329 "DATE_ADD",
1330 Vec::new(),
1331 vec![&function.this, &function.interval],
1332 scope,
1333 dialect,
1334 )),
1335 Expression::DateSub(function) => Some(transform_function_from_parts(
1336 "DATE_SUB",
1337 Vec::new(),
1338 vec![&function.this, &function.interval],
1339 scope,
1340 dialect,
1341 )),
1342 Expression::DateDiff(function) => Some(transform_function_from_parts(
1343 "DATE_DIFF",
1344 Vec::new(),
1345 vec![&function.this, &function.expression],
1346 scope,
1347 dialect,
1348 )),
1349 _ => None,
1350 }
1351}
1352
1353fn transform_function_from_args(
1354 name: &str,
1355 args: &[Expression],
1356 scope: &Scope,
1357 dialect: DialectType,
1358) -> TransformFunctionFact {
1359 let literal_args = args
1360 .iter()
1361 .filter_map(|arg| literal_argument(arg, dialect))
1362 .collect();
1363 transform_function_from_parts(name, literal_args, args.iter().collect(), scope, dialect)
1364}
1365
1366fn transform_function_from_parts(
1367 name: &str,
1368 literal_args: Vec<String>,
1369 args: Vec<&Expression>,
1370 scope: &Scope,
1371 _dialect: DialectType,
1372) -> TransformFunctionFact {
1373 let column_args = dedupe_column_refs(
1374 args.into_iter()
1375 .flat_map(|arg| fallback_column_references(arg, scope))
1376 .collect(),
1377 );
1378
1379 TransformFunctionFact {
1380 name: name.to_string(),
1381 literal_args,
1382 column_args,
1383 }
1384}
1385
1386fn literal_argument(expression: &Expression, dialect: DialectType) -> Option<String> {
1387 match expression {
1388 Expression::Literal(literal) => Some(literal.value_str().to_string()),
1389 Expression::Boolean(boolean) => Some(boolean.value.to_string()),
1390 Expression::Null(_) => Some("NULL".to_string()),
1391 Expression::Identifier(identifier) => Some(identifier.name.clone()),
1392 Expression::Var(var) => Some(var.this.clone()),
1393 Expression::DataType(data_type) => render_data_type(data_type, dialect),
1394 _ => None,
1395 }
1396}
1397
1398fn datetime_field_name(field: &crate::expressions::DateTimeField) -> String {
1399 match field {
1400 crate::expressions::DateTimeField::Year => "year".to_string(),
1401 crate::expressions::DateTimeField::Month => "month".to_string(),
1402 crate::expressions::DateTimeField::Day => "day".to_string(),
1403 crate::expressions::DateTimeField::Hour => "hour".to_string(),
1404 crate::expressions::DateTimeField::Minute => "minute".to_string(),
1405 crate::expressions::DateTimeField::Second => "second".to_string(),
1406 crate::expressions::DateTimeField::Millisecond => "millisecond".to_string(),
1407 crate::expressions::DateTimeField::Microsecond => "microsecond".to_string(),
1408 crate::expressions::DateTimeField::DayOfWeek => "day_of_week".to_string(),
1409 crate::expressions::DateTimeField::DayOfYear => "day_of_year".to_string(),
1410 crate::expressions::DateTimeField::Week => "week".to_string(),
1411 crate::expressions::DateTimeField::WeekWithModifier(modifier) => {
1412 format!("week({modifier})")
1413 }
1414 crate::expressions::DateTimeField::Quarter => "quarter".to_string(),
1415 crate::expressions::DateTimeField::Epoch => "epoch".to_string(),
1416 crate::expressions::DateTimeField::Timezone => "timezone".to_string(),
1417 crate::expressions::DateTimeField::TimezoneHour => "timezone_hour".to_string(),
1418 crate::expressions::DateTimeField::TimezoneMinute => "timezone_minute".to_string(),
1419 crate::expressions::DateTimeField::Date => "date".to_string(),
1420 crate::expressions::DateTimeField::Time => "time".to_string(),
1421 crate::expressions::DateTimeField::Custom(name) => name.clone(),
1422 }
1423}
1424
1425fn unwrap_projection_alias(expression: &Expression) -> &Expression {
1426 match expression {
1427 Expression::Alias(alias) => unwrap_projection_alias(&alias.this),
1428 Expression::Annotated(annotated) => unwrap_projection_alias(&annotated.this),
1429 Expression::Paren(paren) => unwrap_projection_alias(&paren.this),
1430 _ => expression,
1431 }
1432}
1433
1434fn projection_name(expression: &Expression) -> Option<String> {
1435 match expression {
1436 Expression::Alias(alias) => Some(alias.alias.name.clone()),
1437 Expression::Column(column) => Some(column.name.name.clone()),
1438 Expression::Identifier(identifier) => Some(identifier.name.clone()),
1439 Expression::Star(_) => Some("*".to_string()),
1440 Expression::Annotated(annotated) => projection_name(&annotated.this),
1441 _ => None,
1442 }
1443}
1444
1445fn projection_is_star(expression: &Expression) -> bool {
1446 matches!(expression, Expression::Star(_))
1447 || matches!(expression, Expression::Column(column) if column.name.name == "*")
1448}
1449
1450fn projection_star_table(expression: &Expression) -> Option<String> {
1451 match expression {
1452 Expression::Star(star) => star
1453 .table
1454 .as_ref()
1455 .map(|identifier| identifier.name.clone()),
1456 Expression::Column(column) if column.name.name == "*" => column
1457 .table
1458 .as_ref()
1459 .map(|identifier| identifier.name.clone()),
1460 _ => None,
1461 }
1462}
1463
1464fn transform_kind(expression: &Expression) -> TransformKind {
1465 if projection_is_star(expression) {
1466 TransformKind::Star
1467 } else if is_cast_expression(expression) {
1468 TransformKind::Cast
1469 } else if contains_aggregate(expression) {
1470 TransformKind::Aggregation
1471 } else if matches!(
1472 expression,
1473 Expression::Column(_) | Expression::Identifier(_)
1474 ) {
1475 TransformKind::Direct
1476 } else if is_simple_constant(expression) {
1477 TransformKind::Constant
1478 } else {
1479 TransformKind::Expression
1480 }
1481}
1482
1483fn is_cast_expression(expression: &Expression) -> bool {
1484 matches!(
1485 expression,
1486 Expression::Cast(_) | Expression::TryCast(_) | Expression::SafeCast(_)
1487 )
1488}
1489
1490fn cast_type(expression: &Expression, dialect: DialectType) -> Option<String> {
1491 match expression {
1492 Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
1493 render_data_type(&cast.to, dialect)
1494 }
1495 _ => None,
1496 }
1497}
1498
1499fn render_data_type(data_type: &DataType, dialect: DialectType) -> Option<String> {
1500 if dialect == DialectType::ClickHouse {
1501 return crate::generator::Generator::with_config(
1502 Dialect::get(dialect).generator_config().clone(),
1503 )
1504 .generate_type_hint(data_type)
1505 .ok();
1506 }
1507 Dialect::get(dialect)
1508 .generate(&Expression::DataType(data_type.clone()))
1509 .ok()
1510}
1511
1512fn is_simple_constant(expression: &Expression) -> bool {
1513 match expression {
1514 Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_) => true,
1515 Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
1516 is_simple_constant(&cast.this)
1517 }
1518 Expression::Neg(unary) | Expression::BitwiseNot(unary) => is_simple_constant(&unary.this),
1519 _ => false,
1520 }
1521}
1522
1523impl NullabilityContext<'_> {
1524 const MAX_DEPTH: usize = 128;
1527
1528 fn output(&self, scope_id: usize, ordinal: usize, depth: usize) -> ProjectionNullability {
1529 let key = (scope_id, ordinal);
1530 if let Some(value) = self.outputs.borrow().get(&key) {
1531 return *value;
1532 }
1533 if depth >= Self::MAX_DEPTH || !self.resolving.borrow_mut().insert(key) {
1534 return ProjectionNullability::Unknown;
1535 }
1536 let value = self.output_inner(scope_id, ordinal, depth + 1);
1537 self.resolving.borrow_mut().remove(&key);
1538 self.outputs.borrow_mut().insert(key, value);
1539 value
1540 }
1541
1542 fn output_inner(&self, scope_id: usize, ordinal: usize, depth: usize) -> ProjectionNullability {
1543 use ProjectionNullability::*;
1544 let frame = &self.scopes[scope_id];
1545 let query = crate::scope::scope_query(&frame.scope.expression);
1546 if let Expression::Select(select) = query {
1547 return select
1548 .expressions
1549 .get(ordinal)
1550 .map(|expression| self.expression(scope_id, expression, depth))
1551 .unwrap_or(Unknown);
1552 }
1553 if !is_set_operation(query) || frame.branches.len() != 2 {
1554 return Unknown;
1555 }
1556 let (left, right) =
1557 match crate::set_operation::set_operation_layout(query, Some(self.dialect)) {
1558 Ok(Some(layout)) => match layout.outputs.get(ordinal) {
1559 Some(output) => (output.left_ordinal, output.right_ordinal),
1560 None => return Unknown,
1561 },
1562 Ok(None) => (Some(ordinal), Some(ordinal)),
1563 Err(_) => return Unknown,
1564 };
1565 let branch = |id, ordinal: Option<usize>| {
1566 ordinal
1567 .map(|ordinal| self.output(id, ordinal, depth))
1568 .unwrap_or(Nullable)
1569 };
1570 let left = branch(frame.branches[0], left);
1571 if matches!(query, Expression::Except(_)) {
1572 return left;
1573 }
1574 let right = branch(frame.branches[1], right);
1575 if matches!(query, Expression::Intersect(_)) {
1576 return match (left, right) {
1577 (NonNull, _) | (_, NonNull) => NonNull,
1578 (Nullable, Nullable) => Nullable,
1579 _ => Unknown,
1580 };
1581 }
1582 match (left, right) {
1583 (NonNull, NonNull) => NonNull,
1584 (Nullable, _) | (_, Nullable) => Nullable,
1585 _ => Unknown,
1586 }
1587 }
1588
1589 fn expression(
1590 &self,
1591 scope_id: usize,
1592 expression: &Expression,
1593 depth: usize,
1594 ) -> ProjectionNullability {
1595 use ProjectionNullability::*;
1596 if depth >= Self::MAX_DEPTH {
1597 return Unknown;
1598 }
1599 let depth = depth + 1;
1600 match expression {
1601 Expression::Alias(alias) => self.expression(scope_id, &alias.this, depth),
1602 Expression::Annotated(annotated) => self.expression(scope_id, &annotated.this, depth),
1603 Expression::Paren(paren) => self.expression(scope_id, &paren.this, depth),
1604 Expression::Literal(_)
1605 | Expression::Boolean(_)
1606 | Expression::Count(_)
1607 | Expression::CountIf(_) => NonNull,
1608 Expression::Null(_) => Nullable,
1609 Expression::Cast(cast) => self.expression(scope_id, &cast.this, depth),
1610 Expression::Column(column) => {
1611 if column.span.or(column.name.span).is_some_and(|span| {
1612 self.uncertain_columns.contains_key(&(span.start, span.end))
1613 }) {
1614 Unknown
1615 } else {
1616 self.column(scope_id, &column.name, column.table.as_ref(), depth)
1617 }
1618 }
1619 Expression::Identifier(identifier) => self.column(scope_id, identifier, None, depth),
1620 Expression::Coalesce(func) => {
1621 let mut all_nullable = !func.expressions.is_empty();
1622 for expression in &func.expressions {
1623 match self.expression(scope_id, expression, depth) {
1624 NonNull => return NonNull,
1625 Nullable => {}
1626 Unknown => all_nullable = false,
1627 }
1628 }
1629 if all_nullable {
1630 Nullable
1631 } else {
1632 Unknown
1633 }
1634 }
1635 _ => Unknown,
1636 }
1637 }
1638
1639 fn source_scope(
1640 &self,
1641 frame: &NullabilityScope<'_>,
1642 name: &str,
1643 source: &SourceInfo,
1644 ) -> Option<usize> {
1645 if let Some(Expression::Table(table)) = frame.bindings.get(name).copied() {
1646 if table.schema.is_none() && table.catalog.is_none() {
1647 return frame
1648 .ctes
1649 .get(&crate::set_operation::identifier_key(
1650 &table.name,
1651 Some(self.dialect),
1652 ))
1653 .copied();
1654 }
1655 }
1656 if source.kind == SourceKind::DerivedTable && source.is_scope {
1657 return frame.derived.iter().copied().find(|id| {
1658 crate::scope::scope_query(&self.scopes[*id].scope.expression)
1659 == crate::scope::scope_query(&source.expression)
1660 });
1661 }
1662 None
1663 }
1664
1665 fn source_columns(
1666 &self,
1667 frame: &NullabilityScope<'_>,
1668 name: &str,
1669 source: &SourceInfo,
1670 ) -> Option<Vec<Identifier>> {
1671 if let Some(id) = self.source_scope(frame, name, source) {
1672 let expression = if matches!(
1673 frame.bindings.get(name).copied(),
1674 Some(Expression::Table(_))
1675 ) {
1676 &self.scopes[id].scope.expression
1677 } else {
1678 &source.expression
1679 };
1680 return crate::set_operation::query_output_identifiers(expression, Some(self.dialect))
1681 .ok();
1682 }
1683 if source.kind == SourceKind::Table {
1684 let mut resolver = crate::resolver::Resolver::new(
1685 &frame.selected,
1686 self.mapping_schema.unwrap_or(&self.empty_schema),
1687 false,
1688 );
1689 let columns = resolver.get_source_columns(name).ok()?;
1690 if columns.is_empty() || columns.iter().any(|name| name == "*") {
1691 return None;
1692 }
1693 Some(columns.into_iter().map(Identifier::new).collect())
1694 } else {
1695 crate::set_operation::query_output_identifiers(&source.expression, Some(self.dialect))
1696 .ok()
1697 }
1698 }
1699
1700 fn column(
1701 &self,
1702 scope_id: usize,
1703 column: &Identifier,
1704 qualifier: Option<&Identifier>,
1705 depth: usize,
1706 ) -> ProjectionNullability {
1707 use ProjectionNullability::*;
1708 let frame = &self.scopes[scope_id];
1709 let column_key = crate::set_operation::identifier_key(column, Some(self.dialect));
1710 let same_column = |name: &Identifier| {
1711 crate::set_operation::identifier_key(name, Some(self.dialect)) == column_key
1712 };
1713 let binding = if let Some(qualifier) = qualifier {
1714 let key = crate::set_operation::identifier_key(qualifier, Some(self.dialect));
1715 let mut matches = frame
1716 .bindings
1717 .iter()
1718 .filter(|(_, expression)| {
1719 expression_source_identifier(expression).is_some_and(|identifier| {
1720 crate::set_operation::identifier_key(identifier, Some(self.dialect)) == key
1721 })
1722 })
1723 .filter_map(|(name, _)| frame.selected.sources.get_key_value(name));
1724 let binding = matches.next();
1725 if matches.next().is_some() {
1726 return Unknown;
1727 }
1728 binding
1729 } else {
1730 let mut binding = None;
1731 for (name, source) in &frame.selected.sources {
1732 let Some(columns) = self.source_columns(frame, name, source) else {
1735 return Unknown;
1736 };
1737 if columns.iter().any(&same_column) {
1738 if binding.is_some() {
1739 return Unknown;
1740 }
1741 binding = Some((name, source));
1742 }
1743 }
1744 binding
1745 };
1746 let Some((name, source)) = binding else {
1747 return Unknown;
1748 };
1749 if frame
1750 .bindings
1751 .get(name)
1752 .and_then(|expression| expression_source_identifier(expression))
1753 .is_some_and(|identifier| {
1754 frame
1755 .nullable_sources
1756 .contains(&crate::set_operation::identifier_key(
1757 identifier,
1758 Some(self.dialect),
1759 ))
1760 })
1761 {
1762 return Nullable;
1763 }
1764 let target = self.source_scope(frame, name, source);
1765 if target.is_none() && source.kind == SourceKind::Table {
1766 let info = self.schema.and_then(|schema| {
1767 source_table_name(source).and_then(|table| schema.column(&table, &column.name))
1768 });
1769 return match info {
1770 Some(info) if info.primary_key || info.nullable == Some(false) => NonNull,
1771 Some(info) if info.nullable == Some(true) => Nullable,
1772 _ => Unknown,
1773 };
1774 }
1775 let Some(columns) = self.source_columns(frame, name, source) else {
1776 return Unknown;
1777 };
1778 let mut ordinals = columns
1779 .iter()
1780 .enumerate()
1781 .filter(|(_, name)| same_column(name))
1782 .map(|(ordinal, _)| ordinal);
1783 let Some(ordinal) = ordinals.next() else {
1784 return Unknown;
1785 };
1786 if ordinals.next().is_some() {
1787 return Unknown;
1788 }
1789 target
1790 .map(|id| self.output(id, ordinal, depth))
1791 .unwrap_or(Unknown)
1792 }
1793}
1794
1795fn terminal_references_from_lineage(
1797 node: &LineageNode,
1798 path: Option<&PathTransform>,
1799) -> Vec<ColumnReferenceFact> {
1800 let mut refs = Vec::new();
1801 collect_terminal_references(node, path, &mut refs);
1802 dedupe_column_refs(refs)
1803}
1804
1805fn collect_terminal_references(
1806 node: &LineageNode,
1807 path: Option<&PathTransform>,
1808 refs: &mut Vec<ColumnReferenceFact>,
1809) {
1810 let stepped;
1812 let path = match path {
1813 Some(path) if !is_set_operation(&node.expression) => {
1814 stepped = path.step(unwrap_projection_alias(&node.expression));
1815 Some(&stepped)
1816 }
1817 path => path,
1818 };
1819 if node.downstream.is_empty() {
1820 if let Some(mut reference) = column_reference_from_lineage_node(node) {
1821 if let Some(path) = path {
1822 path.apply(&mut reference);
1823 }
1824 refs.push(reference);
1825 }
1826 return;
1827 }
1828
1829 for child in &node.downstream {
1830 collect_terminal_references(child, path, refs);
1831 }
1832}
1833
1834#[derive(Clone)]
1836struct PathTransform {
1837 dialect: DialectType,
1838 kind: TransformKind,
1839 functions: Vec<String>,
1840 window: bool,
1841}
1842
1843impl PathTransform {
1844 fn new(dialect: DialectType) -> Self {
1845 Self {
1846 dialect,
1847 kind: TransformKind::Direct,
1848 functions: Vec::new(),
1849 window: false,
1850 }
1851 }
1852
1853 fn step(&self, expression: &Expression) -> Self {
1855 let mut next = self.clone();
1856 for node in crate::scope::walk_in_scope(expression, false) {
1859 next.kind = combine_transform_kinds(next.kind, path_node_kind(node));
1860 if matches!(node, Expression::WindowFunction(_)) {
1861 next.window = true;
1862 } else if let Some(name) = function_name(node, self.dialect) {
1863 if !next.functions.contains(&name) {
1864 next.functions.push(name);
1865 }
1866 }
1867 }
1868 next
1869 }
1870
1871 fn apply(&self, reference: &mut ColumnReferenceFact) {
1872 reference.path_transform_kind = Some(self.kind);
1873 reference.path_functions = self.functions.clone();
1874 reference.path_window = self.window;
1875 }
1876}
1877
1878fn path_node_kind(node: &Expression) -> TransformKind {
1881 if is_aggregate(node) {
1882 return TransformKind::Aggregation;
1883 }
1884 match node {
1885 Expression::Column(_)
1886 | Expression::Identifier(_)
1887 | Expression::Alias(_)
1888 | Expression::Paren(_)
1889 | Expression::Annotated(_)
1890 | Expression::Subquery(_)
1891 | Expression::Select(_)
1892 | Expression::Union(_)
1893 | Expression::Intersect(_)
1894 | Expression::Except(_)
1895 | Expression::Cte(_) => TransformKind::Direct,
1896 Expression::Star(_) => TransformKind::Star,
1897 Expression::Cast(_) | Expression::TryCast(_) | Expression::SafeCast(_) => {
1898 TransformKind::Cast
1899 }
1900 Expression::Literal(_)
1901 | Expression::Boolean(_)
1902 | Expression::Null(_)
1903 | Expression::DataType(_) => TransformKind::Constant,
1904 _ => TransformKind::Expression,
1905 }
1906}
1907
1908fn function_name(node: &Expression, dialect: DialectType) -> Option<String> {
1910 match node {
1911 Expression::Function(function) => {
1912 if !function.qualified_name.is_empty() {
1913 return function
1914 .qualified_name
1915 .iter()
1916 .map(|identifier| {
1917 crate::generate(&Expression::Identifier(identifier.clone()), dialect).ok()
1918 })
1919 .collect::<Option<Vec<_>>>()
1920 .map(|parts| parts.join("."));
1921 }
1922 if function.quoted {
1923 return crate::generate(
1924 &Expression::Identifier(Identifier::quoted(&function.name)),
1925 dialect,
1926 )
1927 .ok();
1928 }
1929 if function.name.contains('.') {
1930 return Some(function.name.clone());
1931 }
1932 use crate::generator::NormalizeFunctions;
1933 Some(
1934 match Dialect::get(dialect).generator_config().normalize_functions {
1935 NormalizeFunctions::Upper => function.name.to_uppercase(),
1936 NormalizeFunctions::Lower => function.name.to_lowercase(),
1937 NormalizeFunctions::None => function.name.clone(),
1938 },
1939 )
1940 }
1941 Expression::AggregateFunction(function) => Some(function.name.to_uppercase()),
1942 Expression::Column(_)
1943 | Expression::Identifier(_)
1944 | Expression::Literal(_)
1945 | Expression::Alias(_)
1946 | Expression::Paren(_)
1947 | Expression::Star(_)
1948 | Expression::Cast(_)
1949 | Expression::TryCast(_)
1950 | Expression::SafeCast(_)
1951 | Expression::Subquery(_)
1952 | Expression::Exists(_)
1953 | Expression::Select(_)
1954 | Expression::Case(_) => None,
1955 node if is_arithmetic(node) || is_comparison(node) || is_logical(node) => None,
1956 _ => {
1958 let sql = crate::generate(node, dialect).ok()?;
1959 let (name, _) = sql.split_once('(')?;
1960 let is_name =
1961 !name.is_empty() && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
1962 is_name.then(|| name.to_uppercase())
1963 }
1964 }
1965}
1966
1967fn combine_transform_kinds(left: TransformKind, right: TransformKind) -> TransformKind {
1968 let rank = |kind| match kind {
1969 TransformKind::Constant => 0,
1970 TransformKind::Direct => 1,
1971 TransformKind::Star => 2,
1972 TransformKind::Cast => 3,
1973 TransformKind::Expression => 4,
1974 TransformKind::Aggregation => 5,
1975 };
1976 if rank(right) > rank(left) {
1977 right
1978 } else {
1979 left
1980 }
1981}
1982
1983fn column_reference_from_lineage_node(node: &LineageNode) -> Option<ColumnReferenceFact> {
1984 match &node.expression {
1985 Expression::Column(column) => {
1986 let source_name = non_empty_string(node.source_name.clone());
1987 let table =
1988 lineage_node_table(node).or_else(|| column.table.as_ref().map(|t| t.name.clone()));
1989 let confidence = if node.source_kind == SourceKind::Unknown && source_name.is_none() {
1990 ReferenceConfidence::Unknown
1991 } else {
1992 ReferenceConfidence::Resolved
1993 };
1994 Some(ColumnReferenceFact {
1995 source_name,
1996 source_alias: node.source_alias.clone(),
1997 source_kind: node.source_kind,
1998 table,
1999 column: column.name.name.clone(),
2000 unqualified: column.table.is_none(),
2001 confidence,
2002 path_transform_kind: None,
2003 path_functions: Vec::new(),
2004 path_window: false,
2005 })
2006 }
2007 Expression::Star(_) => Some(ColumnReferenceFact {
2008 source_name: non_empty_string(node.source_name.clone()),
2009 source_alias: node.source_alias.clone(),
2010 source_kind: node.source_kind,
2011 table: lineage_node_table(node),
2012 column: "*".to_string(),
2013 unqualified: true,
2014 confidence: if node.source_kind == SourceKind::Unknown {
2015 ReferenceConfidence::Unknown
2016 } else {
2017 ReferenceConfidence::Resolved
2018 },
2019 path_transform_kind: None,
2020 path_functions: Vec::new(),
2021 path_window: false,
2022 }),
2023 _ => None,
2024 }
2025}
2026
2027fn lineage_node_table(node: &LineageNode) -> Option<String> {
2028 match &node.source {
2029 Expression::Table(table) => Some(table_name(table)),
2030 _ => None,
2031 }
2032}
2033
2034fn fallback_column_references(expression: &Expression, scope: &Scope) -> Vec<ColumnReferenceFact> {
2035 let mut refs = Vec::new();
2036 let source_count = scope.sources.len();
2037 let single_source = if source_count == 1 {
2038 scope.sources.iter().next()
2039 } else {
2040 None
2041 };
2042
2043 for column_expr in expression.find_all(|candidate| matches!(candidate, Expression::Column(_))) {
2044 if let Expression::Column(column) = column_expr {
2045 if column.name.name == "*" {
2046 continue;
2047 }
2048 let source = column
2049 .table
2050 .as_ref()
2051 .and_then(|table| scope.sources.get(&table.name));
2052 let (source_name, source_alias, source_kind, table, confidence) =
2053 if let Some(table_identifier) = &column.table {
2054 if let Some(source) = source {
2055 (
2056 Some(table_identifier.name.clone()),
2057 source.alias.clone(),
2058 source.kind,
2059 source_table_name(source)
2060 .or_else(|| Some(table_identifier.name.clone())),
2061 ReferenceConfidence::Resolved,
2062 )
2063 } else {
2064 (
2065 Some(table_identifier.name.clone()),
2066 None,
2067 SourceKind::Unknown,
2068 Some(table_identifier.name.clone()),
2069 ReferenceConfidence::Unknown,
2070 )
2071 }
2072 } else if let Some((name, source)) = single_source {
2073 (
2074 Some(name.clone()),
2075 source.alias.clone(),
2076 source.kind,
2077 source_table_name(source).or_else(|| Some(name.clone())),
2078 ReferenceConfidence::Resolved,
2079 )
2080 } else if source_count > 1 {
2081 (
2082 None,
2083 None,
2084 SourceKind::Unknown,
2085 None,
2086 ReferenceConfidence::Ambiguous,
2087 )
2088 } else {
2089 (
2090 None,
2091 None,
2092 SourceKind::Unknown,
2093 None,
2094 ReferenceConfidence::Unknown,
2095 )
2096 };
2097
2098 refs.push(ColumnReferenceFact {
2099 source_name,
2100 source_alias,
2101 source_kind,
2102 table,
2103 column: column.name.name.clone(),
2104 unqualified: column.table.is_none(),
2105 confidence,
2106 path_transform_kind: None,
2107 path_functions: Vec::new(),
2108 path_window: false,
2109 });
2110 }
2111 }
2112
2113 dedupe_column_refs(refs)
2114}
2115
2116fn dedupe_column_refs(refs: Vec<ColumnReferenceFact>) -> Vec<ColumnReferenceFact> {
2117 let mut seen = HashMap::new();
2118 let mut deduped = Vec::new();
2119
2120 for reference in refs {
2121 let key = (
2122 reference.source_name.clone(),
2123 reference.source_alias.clone(),
2124 reference.table.clone(),
2125 reference.column.clone(),
2126 format!("{:?}", reference.source_kind),
2127 reference.unqualified,
2128 format!("{:?}", reference.confidence),
2129 );
2130 if let Some(&index) = seen.get(&key) {
2131 let existing: &mut ColumnReferenceFact = &mut deduped[index];
2133 if let (Some(left), Some(right)) =
2134 (existing.path_transform_kind, reference.path_transform_kind)
2135 {
2136 existing.path_transform_kind = Some(combine_transform_kinds(left, right));
2137 }
2138 for function in reference.path_functions {
2139 if !existing.path_functions.contains(&function) {
2140 existing.path_functions.push(function);
2141 }
2142 }
2143 existing.path_window |= reference.path_window;
2144 } else {
2145 seen.insert(key, deduped.len());
2146 deduped.push(reference);
2147 }
2148 }
2149
2150 deduped
2151}
2152
2153fn relation_facts(
2154 scope: &Scope,
2155 mapping_schema: Option<&crate::schema::MappingSchema>,
2156 dialect: DialectType,
2157) -> Vec<RelationFact> {
2158 let mut relations = Vec::new();
2159 let mut seen = HashSet::new();
2160 collect_relation_facts(scope, mapping_schema, dialect, &mut seen, &mut relations);
2161
2162 relations.sort_by(|left, right| {
2163 left.name
2164 .cmp(&right.name)
2165 .then_with(|| left.alias.cmp(&right.alias))
2166 });
2167 relations
2168}
2169
2170fn collect_relation_facts(
2171 scope: &Scope,
2172 mapping_schema: Option<&crate::schema::MappingSchema>,
2173 dialect: DialectType,
2174 seen: &mut HashSet<String>,
2175 relations: &mut Vec<RelationFact>,
2176) {
2177 for relation in scope.sources.iter().map(|(source_name, source)| {
2178 let identity = source_table_identity(source);
2179 RelationFact {
2180 name: source
2181 .lineage_name
2182 .clone()
2183 .or_else(|| identity.as_ref().map(|identity| identity.name.clone()))
2184 .unwrap_or_else(|| source_name.clone()),
2185 alias: source.alias.clone().or_else(|| source_alias(source)),
2186 kind: source.kind,
2187 columns: source_columns(source, mapping_schema, dialect),
2188 catalog: identity
2189 .as_ref()
2190 .and_then(|identity| identity.catalog.clone()),
2191 schema: identity
2192 .as_ref()
2193 .and_then(|identity| identity.schema.clone()),
2194 table: identity
2195 .as_ref()
2196 .and_then(|identity| identity.table.clone()),
2197 }
2198 }) {
2199 let key = format!("{:?}|{}|{:?}", relation.kind, relation.name, relation.alias);
2200 if seen.insert(key) {
2201 relations.push(relation);
2202 }
2203 }
2204
2205 for branch_scope in &scope.union_scopes {
2206 collect_relation_facts(branch_scope, mapping_schema, dialect, seen, relations);
2207 }
2208}
2209
2210fn base_table_facts(
2211 scope: &Scope,
2212 mapping_schema: Option<&crate::schema::MappingSchema>,
2213 dialect: DialectType,
2214) -> Vec<RelationFact> {
2215 let mut relations = Vec::new();
2216 let mut seen = HashSet::new();
2217
2218 collect_base_table_facts(scope, mapping_schema, dialect, &mut seen, &mut relations);
2219
2220 relations.sort_by(|left, right| left.name.cmp(&right.name));
2221 relations
2222}
2223
2224fn collect_base_table_facts(
2225 scope: &Scope,
2226 mapping_schema: Option<&crate::schema::MappingSchema>,
2227 dialect: DialectType,
2228 seen: &mut HashSet<String>,
2229 relations: &mut Vec<RelationFact>,
2230) {
2231 for source in scope.sources.values() {
2232 if source.kind != SourceKind::Table {
2233 continue;
2234 }
2235
2236 let Some(identity) = source_table_identity(source) else {
2237 continue;
2238 };
2239
2240 if seen.insert(identity.name.clone()) {
2241 relations.push(RelationFact {
2242 name: identity.name,
2243 alias: source.alias.clone().or_else(|| source_alias(source)),
2244 kind: SourceKind::Table,
2245 columns: source_columns(source, mapping_schema, dialect),
2246 catalog: identity.catalog,
2247 schema: identity.schema,
2248 table: identity.table,
2249 });
2250 }
2251 }
2252
2253 for child_scope in scope
2254 .cte_scopes
2255 .iter()
2256 .chain(scope.union_scopes.iter())
2257 .chain(scope.table_scopes.iter())
2258 .chain(scope.derived_table_scopes.iter())
2259 .chain(scope.subquery_scopes.iter())
2260 {
2261 collect_base_table_facts(child_scope, mapping_schema, dialect, seen, relations);
2262 }
2263}
2264
2265fn source_columns(
2266 source: &SourceInfo,
2267 mapping_schema: Option<&crate::schema::MappingSchema>,
2268 dialect: DialectType,
2269) -> Vec<String> {
2270 match &source.expression {
2271 Expression::Table(table) => mapping_schema
2272 .and_then(|schema| schema.column_names(&table_name(table)).ok())
2273 .unwrap_or_default(),
2274 Expression::Select(_)
2275 | Expression::Union(_)
2276 | Expression::Intersect(_)
2277 | Expression::Except(_) => {
2278 get_output_column_names_for_dialect(&source.expression, Some(dialect))
2279 }
2280 Expression::Subquery(subquery) => {
2281 get_output_column_names_for_dialect(&subquery.this, Some(dialect))
2282 }
2283 Expression::Cte(cte) if !cte.columns.is_empty() => cte
2284 .columns
2285 .iter()
2286 .map(|column| column.name.clone())
2287 .collect(),
2288 Expression::Cte(cte) => get_output_column_names_for_dialect(&cte.this, Some(dialect)),
2289 _ => Vec::new(),
2290 }
2291}
2292
2293fn source_table_name(source: &SourceInfo) -> Option<String> {
2294 source_table_identity(source).map(|identity| identity.name)
2295}
2296
2297fn source_alias(source: &SourceInfo) -> Option<String> {
2298 match &source.expression {
2299 Expression::Table(table) => table.alias.as_ref().map(|alias| alias.name.clone()),
2300 Expression::Subquery(subquery) => subquery.alias.as_ref().map(|alias| alias.name.clone()),
2301 _ => None,
2302 }
2303}
2304
2305fn table_name(table: &TableRef) -> String {
2306 let mut parts = Vec::new();
2307 if let Some(catalog) = &table.catalog {
2308 parts.push(catalog.name.clone());
2309 }
2310 if let Some(schema) = &table.schema {
2311 parts.push(schema.name.clone());
2312 }
2313 parts.push(table.name.name.clone());
2314 parts.join(".")
2315}
2316
2317#[derive(Debug, Clone)]
2318struct RelationIdentity {
2319 name: String,
2320 catalog: Option<String>,
2321 schema: Option<String>,
2322 table: Option<String>,
2323}
2324
2325fn source_table_identity(source: &SourceInfo) -> Option<RelationIdentity> {
2326 match &source.expression {
2327 Expression::Table(table) => Some(table_identity(table)),
2328 _ => None,
2329 }
2330}
2331
2332fn table_identity(table: &TableRef) -> RelationIdentity {
2333 RelationIdentity {
2334 name: table_name(table),
2335 catalog: table.catalog.as_ref().map(|catalog| catalog.name.clone()),
2336 schema: table.schema.as_ref().map(|schema| schema.name.clone()),
2337 table: Some(table.name.name.clone()),
2338 }
2339}
2340
2341fn set_operation_facts(
2342 expression: &Expression,
2343 scope: &Scope,
2344 dialect: DialectType,
2345 nullability: &NullabilityContext<'_>,
2346) -> Vec<SetOperationFact> {
2347 let mut facts = Vec::new();
2348 collect_set_operation_facts(expression, scope, dialect, nullability, &mut facts);
2349 facts
2350}
2351
2352fn collect_set_operation_facts(
2353 expression: &Expression,
2354 scope: &Scope,
2355 dialect: DialectType,
2356 nullability: &NullabilityContext<'_>,
2357 facts: &mut Vec<SetOperationFact>,
2358) {
2359 match expression {
2360 Expression::Union(union) => {
2361 facts.push(SetOperationFact {
2362 kind: "union".to_string(),
2363 all: union.all,
2364 distinct: union.distinct,
2365 output_columns: get_output_column_names_for_dialect(expression, Some(dialect)),
2366 branches: set_operation_branches(
2367 &union.left,
2368 &union.right,
2369 scope,
2370 dialect,
2371 SetOperationBranchRole::Value,
2372 nullability,
2373 ),
2374 });
2375 collect_set_operation_facts(
2376 &union.left,
2377 scope.union_scopes.first().unwrap_or(scope),
2378 dialect,
2379 nullability,
2380 facts,
2381 );
2382 collect_set_operation_facts(
2383 &union.right,
2384 scope.union_scopes.get(1).unwrap_or(scope),
2385 dialect,
2386 nullability,
2387 facts,
2388 );
2389 }
2390 Expression::Intersect(intersect) => {
2391 facts.push(SetOperationFact {
2392 kind: "intersect".to_string(),
2393 all: intersect.all,
2394 distinct: intersect.distinct,
2395 output_columns: get_output_column_names_for_dialect(expression, Some(dialect)),
2396 branches: set_operation_branches(
2397 &intersect.left,
2398 &intersect.right,
2399 scope,
2400 dialect,
2401 SetOperationBranchRole::Filter,
2402 nullability,
2403 ),
2404 });
2405 collect_set_operation_facts(
2406 &intersect.left,
2407 scope.union_scopes.first().unwrap_or(scope),
2408 dialect,
2409 nullability,
2410 facts,
2411 );
2412 collect_set_operation_facts(
2413 &intersect.right,
2414 scope.union_scopes.get(1).unwrap_or(scope),
2415 dialect,
2416 nullability,
2417 facts,
2418 );
2419 }
2420 Expression::Except(except) => {
2421 facts.push(SetOperationFact {
2422 kind: "except".to_string(),
2423 all: except.all,
2424 distinct: except.distinct,
2425 output_columns: get_output_column_names_for_dialect(expression, Some(dialect)),
2426 branches: set_operation_branches(
2427 &except.left,
2428 &except.right,
2429 scope,
2430 dialect,
2431 SetOperationBranchRole::Filter,
2432 nullability,
2433 ),
2434 });
2435 collect_set_operation_facts(
2436 &except.left,
2437 scope.union_scopes.first().unwrap_or(scope),
2438 dialect,
2439 nullability,
2440 facts,
2441 );
2442 collect_set_operation_facts(
2443 &except.right,
2444 scope.union_scopes.get(1).unwrap_or(scope),
2445 dialect,
2446 nullability,
2447 facts,
2448 );
2449 }
2450 Expression::Subquery(subquery) => {
2451 collect_set_operation_facts(&subquery.this, scope, dialect, nullability, facts);
2452 }
2453 _ => {}
2454 }
2455}
2456
2457fn set_operation_branches(
2458 left: &Expression,
2459 right: &Expression,
2460 scope: &Scope,
2461 dialect: DialectType,
2462 right_role: SetOperationBranchRole,
2463 nullability: &NullabilityContext<'_>,
2464) -> Vec<SetOperationBranchFact> {
2465 vec![
2466 SetOperationBranchFact {
2467 index: 0,
2468 role: SetOperationBranchRole::Value,
2469 projections: projection_facts_for_query(
2470 left,
2471 scope.union_scopes.first().unwrap_or(scope),
2472 dialect,
2473 nullability,
2474 ),
2475 },
2476 SetOperationBranchFact {
2477 index: 1,
2478 role: right_role,
2479 projections: projection_facts_for_query(
2480 right,
2481 scope.union_scopes.get(1).unwrap_or(scope),
2482 dialect,
2483 nullability,
2484 ),
2485 },
2486 ]
2487}
2488
2489fn non_empty_string(value: String) -> Option<String> {
2490 if value.is_empty() {
2491 None
2492 } else {
2493 Some(value)
2494 }
2495}