feat(dataflow): model production SQL flow patterns

This commit is contained in:
2026-07-30 18:19:18 +02:00
parent 0664570607
commit e091042948
26 changed files with 2169 additions and 36 deletions
@@ -185,6 +185,8 @@ def _join(context: SchemaPropagationContext) -> SchemaPropagationResult:
field_name="right_keys",
),
]
if node.config.get("join_type", "inner") in {"semi", "anti"}:
return SchemaPropagationResult(left_state, tuple(diagnostics))
prefix = str(node.config.get("right_prefix", "right_"))
prefixed_right = {
f"{prefix}{column}"
@@ -377,6 +379,66 @@ def _expression(context: SchemaPropagationContext) -> SchemaPropagationResult:
return SchemaPropagationResult(state, tuple(diagnostics))
def _calculate(context: SchemaPropagationContext) -> SchemaPropagationResult:
node = context.node
state = context.input_state
diagnostics: list[DataflowDiagnostic] = []
for item in _mapping_items(node.config.get("calculations")):
source = str(item.get("expression") or "")
target = str(item.get("target_column") or "")
try:
parsed = parse_expression(source)
except ExpressionError as exc:
diagnostics.append(
_error(
"expression.invalid",
str(exc),
node_id=node.id,
field="calculations",
)
)
continue
diagnostics.extend(
_unknown_columns(
node,
state,
list(parsed.columns),
field_name="calculations",
)
)
inferred = infer_expression_type(
parsed,
{
name: state.type_of(name) # type: ignore[dict-item]
for name in state.columns
},
)
expected = str(item.get("result_type") or "unknown")
if expected != "unknown" and inferred not in {
"unknown",
"null",
expected,
}:
diagnostics.append(
_warning(
"calculate.type_mismatch",
(
f"Calculation for {target!r} infers {inferred}, "
f"not {expected}."
),
node_id=node.id,
field="calculations",
)
)
if target:
state = _with_column(
state,
target,
expected if expected != "unknown" else inferred,
)
return SchemaPropagationResult(state, tuple(diagnostics))
def _convert_or_replace(
context: SchemaPropagationContext,
) -> SchemaPropagationResult:
@@ -463,6 +525,31 @@ def _sort(context: SchemaPropagationContext) -> SchemaPropagationResult:
)
def _rank(context: SchemaPropagationContext) -> SchemaPropagationResult:
node = context.node
columns = [
*_text_items(node.config.get("partition_by")),
*[
str(item.get("column"))
for item in _mapping_items(node.config.get("order_by"))
if item.get("column")
],
]
diagnostics = _unknown_columns(
node,
context.input_state,
columns,
field_name="order_by",
)
target = str(node.config.get("target_column") or "")
state = (
_with_column(context.input_state, target, "integer")
if target
else context.input_state
)
return SchemaPropagationResult(state, tuple(diagnostics))
def _quality(context: SchemaPropagationContext) -> SchemaPropagationResult:
rules = _mapping_items(context.node.config.get("rules"))
diagnostics = _unknown_columns(
@@ -874,10 +961,12 @@ def register_schema_propagators() -> None:
"select": _select,
"derive": _derive,
"expression": _expression,
"calculate": _calculate,
"convert": _convert_or_replace,
"replace": _convert_or_replace,
"aggregate": _aggregate,
"sort": _sort,
"window.rank": _rank,
"limit": _identity,
"quality.rules": _quality,
"reconcile.compare": _reconcile,