Skip to content

Commit 55c5a90

Browse files
authored
fix: refine nullability after SQL filters (#711)
Co-authored-by: Kevin Longe <273747162+kvlonge@users.noreply.github.com>
1 parent 7b40e17 commit 55c5a90

5 files changed

Lines changed: 507 additions & 43 deletions

File tree

‎src/sqlbuild/compiler/compile/_helpers/analysis/columns.py‎

Lines changed: 71 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
_polyglot_cte_passthrough_facts,
1414
_polyglot_cte_passthrough_nullability_from_parsed,
1515
_polyglot_cte_passthrough_types_from_parsed,
16+
_polyglot_expression_is_non_null_after_filter,
17+
_polyglot_filtered_non_null_outputs,
18+
_polyglot_non_null_filter_context,
1619
)
1720
from sqlbuild.compiler.compile.constants import (
1821
DECIMAL_SQL_TYPE_NAME,
@@ -29,6 +32,7 @@
2932
CompileSqlReference,
3033
CteFactResolvers,
3134
InferredColumn,
35+
NonNullFilterContext,
3236
PolyglotAnalysisResult,
3337
)
3438
from sqlbuild.compiler.compile.types import CompiledResourceType
@@ -483,7 +487,7 @@ def _analyze_columns_and_lineage_with_compact_polyglot(
483487
return None
484488
reference_map: dict[str, tuple[CompiledResourceType, str]] = _lineage_reference_map(references)
485489
relation_alias_by_name: dict[str, str | None] = _compact_relation_alias_by_name(analysis)
486-
cte_passthrough_types, cte_passthrough_nullability, direct_cte_outputs = (
490+
cte_passthrough_types, cte_passthrough_nullability, direct_cte_outputs, parsed = (
487491
_polyglot_cte_passthrough_facts(
488492
polyglot_module=polyglot_module,
489493
cleaned_sql=cleaned_sql,
@@ -500,7 +504,15 @@ def _analyze_columns_and_lineage_with_compact_polyglot(
500504
),
501505
)
502506
if recover_cte_facts
503-
else ({}, {}, frozenset())
507+
else ({}, {}, frozenset(), None)
508+
)
509+
filtered_non_null_outputs: frozenset[str] = _polyglot_filtered_non_null_outputs(
510+
polyglot_module=polyglot_module,
511+
cleaned_sql=cleaned_sql,
512+
dialect=dialect,
513+
analysis=analysis,
514+
column_nullability_by_table=column_nullability_by_table,
515+
parsed=parsed,
504516
)
505517
columns: list[InferredColumn] = []
506518
lineage_columns: list[CompiledLineageColumnFact] = []
@@ -527,12 +539,19 @@ def _analyze_columns_and_lineage_with_compact_polyglot(
527539
)
528540
),
529541
nullability=(
530-
cte_passthrough_nullability.get(output_column)
531-
if output_column in direct_cte_outputs
532-
else None
533-
)
534-
or _compact_projection_nullability(
535-
projection=projection, infer_nullability=infer_nullability
542+
InferredNullability.NON_NULL
543+
if output_column in filtered_non_null_outputs
544+
else (
545+
(
546+
cte_passthrough_nullability.get(output_column)
547+
if output_column in direct_cte_outputs
548+
else None
549+
)
550+
or _compact_projection_nullability(
551+
projection=projection,
552+
infer_nullability=infer_nullability,
553+
)
554+
)
536555
),
537556
)
538557
)
@@ -848,6 +867,10 @@ def _infer_columns_from_polyglot_ast(
848867
alias_nullability_resolver=_polyglot_alias_nullability_from_select,
849868
)
850869
)
870+
non_null_filter_context: NonNullFilterContext | None = _polyglot_non_null_filter_context(
871+
select=select,
872+
column_nullability_by_table=column_nullability_by_table,
873+
)
851874
columns: list[InferredColumn] = []
852875
projection: Any
853876
for projection in getattr(select, "expressions", ()):
@@ -868,14 +891,21 @@ def _infer_columns_from_polyglot_ast(
868891
)
869892
nullability: InferredNullability = InferredNullability.UNKNOWN
870893
if infer_nullability:
871-
nullability = cte_passthrough_nullability.get(
872-
name,
873-
_infer_polyglot_nullability(
894+
nullability = (
895+
InferredNullability.NON_NULL
896+
if _polyglot_expression_is_non_null_after_filter(
874897
expression=inner,
875-
alias_nullability=alias_nullability,
876-
column_nullability_by_table=column_nullability_by_table,
877-
inference_profile=inference_profile,
878-
),
898+
context=non_null_filter_context,
899+
)
900+
else cte_passthrough_nullability.get(
901+
name,
902+
_infer_polyglot_nullability(
903+
expression=inner,
904+
alias_nullability=alias_nullability,
905+
column_nullability_by_table=column_nullability_by_table,
906+
inference_profile=inference_profile,
907+
),
908+
)
879909
)
880910
columns.append(InferredColumn(name=name, type=col_type, nullability=nullability))
881911
return tuple(columns)
@@ -935,6 +965,10 @@ def _analyze_columns_and_lineage_from_polyglot_ast(
935965
if recover_cte_facts
936966
else {}
937967
)
968+
non_null_filter_context: NonNullFilterContext | None = _polyglot_non_null_filter_context(
969+
select=select,
970+
column_nullability_by_table=column_nullability_by_table,
971+
)
938972
columns: list[InferredColumn] = []
939973
lineage_columns: list[CompiledLineageColumnFact] = []
940974
has_star: bool = False
@@ -963,21 +997,28 @@ def _analyze_columns_and_lineage_from_polyglot_ast(
963997
)
964998
nullability: InferredNullability = InferredNullability.UNKNOWN
965999
if infer_nullability:
966-
nullability = cte_passthrough_nullability.get(
967-
output_column,
968-
(
969-
_infer_polyglot_nullability(
970-
expression=inner,
971-
alias_nullability=alias_nullability,
972-
column_nullability_by_table=column_nullability_by_table,
973-
inference_profile=inference_profile,
974-
)
975-
if has_known_nullability
976-
else _infer_polyglot_shallow_nullability(
977-
expression=inner,
978-
inference_profile=inference_profile,
979-
)
980-
),
1000+
nullability = (
1001+
InferredNullability.NON_NULL
1002+
if _polyglot_expression_is_non_null_after_filter(
1003+
expression=inner,
1004+
context=non_null_filter_context,
1005+
)
1006+
else cte_passthrough_nullability.get(
1007+
output_column,
1008+
(
1009+
_infer_polyglot_nullability(
1010+
expression=inner,
1011+
alias_nullability=alias_nullability,
1012+
column_nullability_by_table=column_nullability_by_table,
1013+
inference_profile=inference_profile,
1014+
)
1015+
if has_known_nullability
1016+
else _infer_polyglot_shallow_nullability(
1017+
expression=inner,
1018+
inference_profile=inference_profile,
1019+
)
1020+
),
1021+
)
9811022
)
9821023
columns.append(InferredColumn(name=output_column, type=col_type, nullability=nullability))
9831024

0 commit comments

Comments
 (0)