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